@agifyai/leadify-mcp 8.6.8 → 8.6.10
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 +9 -4
- package/dist/client.js +3 -2
- package/dist/tools/campaigns.d.ts +2 -1
- package/dist/tools/campaigns.js +64 -2
- package/dist/tools/context_entities.d.ts +28 -28
- package/dist/tools/leads.js +16 -0
- package/dist/tools/personas.js +65 -5
- package/dist/tools/schema.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -235,6 +235,7 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
235
235
|
| `get_leads` | Rechercher et lister des leads avec filtres, recherche et pagination, dont le critère composé `event_filter`. |
|
|
236
236
|
| `get_lead` | Récupérer les détails complets d'un lead par son ID. |
|
|
237
237
|
| `update_lead` | Mettre à jour un ou plusieurs champs d'un lead existant. |
|
|
238
|
+
| `clear_lead_field` | Effacer irréversiblement une valeur stockée, sans modifier le schéma du groupe. Exige la confirmation explicite `clear_lead_field`. |
|
|
238
239
|
| `list_events` | Lister les événements tenant-scoped ; les événements archivés sont masqués par défaut. |
|
|
239
240
|
| `get_event` | Lire un événement partagé et son état d’archivage. |
|
|
240
241
|
| `create_event` | Créer un événement partagé avec une clé d’idempotence. |
|
|
@@ -246,7 +247,7 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
246
247
|
| `preview_reset_ai_fields` / `execute_reset_ai_fields` | Prévisualiser puis effacer irréversiblement les champs IA d'un prospect, d'une liste explicite ou d'un groupe explicite, sans toucher aux contacts, flags manuels, activités ou campagnes. |
|
|
247
248
|
| `delete_leads` | Supprimer définitivement des leads par leurs IDs. |
|
|
248
249
|
| `update_schema` | Ajouter ou modifier les définitions de champs d'un groupe. |
|
|
249
|
-
| `delete_columns` | Supprimer des colonnes du schéma et des données d'un groupe. |
|
|
250
|
+
| `delete_columns` | Supprimer des colonnes du schéma et des données d'un groupe. Avec `force: true`, supprimer aussi une clé de données orpheline absente du schéma. |
|
|
250
251
|
| `update_hidden_columns` | Afficher ou masquer des colonnes dans la vue tableau (réversible). |
|
|
251
252
|
| `list_crm_schema_packs` | Lister le catalogue canonique des CRM Schema Packs, leurs domaines et contraintes de compatibilité. |
|
|
252
253
|
| `add_campaign_log` | Enregistrer une entrée de log de campagne pour un lead. |
|
|
@@ -254,10 +255,14 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
254
255
|
| `delete_campaign_log` | Supprimer une entrée de log de campagne. |
|
|
255
256
|
| `update_campaign_stats` | Mettre à jour les statistiques d'email d'une campagne pour un groupe de leads. |
|
|
256
257
|
| `create_campaign` | Créer une campagne DRAFT mono-canal rattachée à un Lead Group qui possède déjà sa paire Persona–Offre. Aucun `persona_id` ou contexte indépendant n’est accepté au niveau Campagne. `channel` (`LINKEDIN` ou `EMAIL`) est obligatoire ; `start_at` inclusif, `end_at` exclusif et `timezone` IANA configurent la fenêtre métier. À `end_at`, Leadify met la campagne en pause réversible (`WINDOW_END`) sans la clôturer. |
|
|
257
|
-
| `update_campaign_configuration` | Modifier la fenêtre de toute campagne encore `OPEN`; le canal reste modifiable uniquement en DRAFT. Prolonger `end_at` dans le futur ou le supprimer relance automatiquement une pause `WINDOW_END` après validation du fournisseur, mais jamais une pause manuelle. La fenêtre demandée reste enregistrée si cette validation échoue. |
|
|
258
|
+
| `update_campaign_configuration` | Modifier le nom, la description et la fenêtre de toute campagne encore `OPEN`; le canal reste modifiable uniquement en DRAFT. Prolonger `end_at` dans le futur ou le supprimer relance automatiquement une pause `WINDOW_END` après validation du fournisseur, mais jamais une pause manuelle. La fenêtre demandée reste enregistrée si cette validation échoue. La Persona est héritée du Lead Group et n’est pas modifiable au niveau Campagne. |
|
|
259
|
+
| `delete_campaign` | Supprimer irréversiblement une campagne `DRAFT` ou `PAUSED` encore `OPEN`, avec la confirmation explicite `delete_campaign`. Une campagne active ou clôturée est refusée, et aucun envoi n’est déclenché. |
|
|
258
260
|
| `list_campaigns` | Lister compactement les campagnes d'une organisation explicitement sélectionnée, avec état effectif, fenêtre, motif de pause et clôture. |
|
|
259
261
|
| `get_campaign` | Récupérer les détails d'une campagne, son état effectif, sa clôture et son éventuel rapport final figé, ainsi que ses KPIs temps réel. |
|
|
260
262
|
| `update_campaign_status` | Changer le statut d'une campagne. `PAUSED` est une pause manuelle réversible ; `COMPLETED` déclenche la clôture définitive terminale et son rapport final immuable. |
|
|
263
|
+
| `get_campaign_audience` | Lire l'audience explicitement enrôlée d'une campagne dans une organisation explicitement sélectionnée, avec les seuls signaux nécessaires à la décision, sans contenu de message ni envoi. |
|
|
264
|
+
| `preview_unenroll_campaign_audience` | Prévisualiser de façon déterministe les membres explicites sans message pour le canal de leur campagne ou sans aucun canal de contact. Retourne les `lead_ids` exacts, sans mutation. |
|
|
265
|
+
| `unenroll_campaign_audience` | Désenrôler uniquement la liste complète de `lead_ids` renvoyée par le preview courant. Relit et refuse toute sélection partielle, étendue ou périmée ; aucun envoi. |
|
|
261
266
|
| `export_campaign` | Exporter les statistiques en CSV ; après clôture définitive, l'export provient du rapport final figé. |
|
|
262
267
|
| `signal_upsert` | Créer ou mettre à jour un signal de business intelligence (INFO, CRITICAL, GOLDEN). Remet le state à `active`. |
|
|
263
268
|
| `signal_expire` | Expirer un signal (événement périmé). Flip de `state` uniquement. |
|
|
@@ -295,8 +300,8 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
295
300
|
| `update_company_brain_sections` | Modifier uniquement les sections globales encore actives (`identity`, `positioning`, `allowedVocabulary`, `prohibitedVocabulary`, `legalConstraints`). Les anciennes sections Offre/Verticale sont filtrées et refusées. |
|
|
296
301
|
| `publish_context_workspace` | Publier une révision prête du Company Brain après confirmation explicite (`confirm_publish: true`). Admin de l'organisation requis ; les raisons de non-readiness sont renvoyées par le serveur. |
|
|
297
302
|
| `list_persona_contracts` / `get_persona_contract` | Lister ou lire les contrats Persona canoniques d’une organisation explicitement sélectionnée. |
|
|
298
|
-
| `create_persona_contract` | Créer un Persona tenant-scoped et lié à une Verticale depuis un contrat canonique complet. |
|
|
299
|
-
| `replace_persona_contract` / `patch_persona_contract` | Remplacer ou modifier un contrat canonique avec verrou optimiste
|
|
303
|
+
| `create_persona_contract` | Créer un Persona tenant-scoped et lié à une Verticale depuis un contrat canonique v2 complet et strict. Toute clé inconnue est refusée. |
|
|
304
|
+
| `replace_persona_contract` / `patch_persona_contract` | Remplacer ou modifier un contrat canonique v2 avec verrou optimiste. Le document est revalidé intégralement avant l’écriture et aucune colonne Persona historique n’est exposée. |
|
|
300
305
|
| `change_persona_status` | Passer un Persona entre `DRAFT`, `ACTIVE` et `ARCHIVED` avec le même verrou `updated_at`, readiness et protection des références. |
|
|
301
306
|
| `list_data_sources` | Lister toutes les sources de données configurées (par pays puis nom). |
|
|
302
307
|
| `create_data_source` | Créer une nouvelle source de données (admin uniquement). |
|
package/dist/client.js
CHANGED
|
@@ -22,12 +22,13 @@ export class LeadifyClient {
|
|
|
22
22
|
init.body = JSON.stringify(body);
|
|
23
23
|
}
|
|
24
24
|
const response = await fetch(url.toString(), init);
|
|
25
|
+
const text = await response.text();
|
|
25
26
|
let data;
|
|
26
27
|
try {
|
|
27
|
-
data =
|
|
28
|
+
data = JSON.parse(text);
|
|
28
29
|
}
|
|
29
30
|
catch {
|
|
30
|
-
data = { raw:
|
|
31
|
+
data = { raw: text };
|
|
31
32
|
}
|
|
32
33
|
if (!response.ok) {
|
|
33
34
|
throw new LeadifyApiError(response.status, data);
|
|
@@ -3,5 +3,6 @@ import { type LeadifyClient } from "../client.js";
|
|
|
3
3
|
type CampaignReadClient = Pick<LeadifyClient, "get">;
|
|
4
4
|
type CampaignWriteClient = Pick<LeadifyClient, "post">;
|
|
5
5
|
type CampaignMutationClient = Pick<LeadifyClient, "put">;
|
|
6
|
-
|
|
6
|
+
type CampaignDeleteClient = Pick<LeadifyClient, "delete">;
|
|
7
|
+
export declare function registerCampaignTools(server: McpServer, readClient?: CampaignReadClient, writeClient?: CampaignWriteClient, mutationClient?: CampaignMutationClient, deleteClient?: CampaignDeleteClient): void;
|
|
7
8
|
export {};
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -27,7 +27,7 @@ function assertCampaignBelongsToOrganization(data, organizationId) {
|
|
|
27
27
|
}
|
|
28
28
|
return campaign;
|
|
29
29
|
}
|
|
30
|
-
export function registerCampaignTools(server, readClient, writeClient, mutationClient) {
|
|
30
|
+
export function registerCampaignTools(server, readClient, writeClient, mutationClient, deleteClient) {
|
|
31
31
|
// ── update_campaign_stats ──────────────────────────────────────────────
|
|
32
32
|
server.tool("update_campaign_stats", "Update email campaign statistics for a lead group. If the given campaignSlug " +
|
|
33
33
|
"doesn't exist in the group's stats yet, it is created; otherwise provided fields " +
|
|
@@ -140,7 +140,9 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
140
140
|
start_at: z.string().datetime().nullable().optional().describe("Inclusive UTC start timestamp, or null to remove the lower bound."),
|
|
141
141
|
end_at: z.string().datetime().nullable().optional().describe("Exclusive UTC end timestamp, or null to remove the upper bound. A future value or null can auto-resume only a WINDOW_END pause."),
|
|
142
142
|
timezone: z.string().min(1).optional().describe("IANA timezone used for the business window, for example Europe/Paris."),
|
|
143
|
-
|
|
143
|
+
name: z.string().min(1).optional().describe("New campaign name."),
|
|
144
|
+
description: z.string().nullable().optional().describe("New campaign description, or null to clear it."),
|
|
145
|
+
}, async ({ id, channel, start_at, end_at, timezone, name, description }) => {
|
|
144
146
|
try {
|
|
145
147
|
const body = {};
|
|
146
148
|
if (channel !== undefined)
|
|
@@ -151,12 +153,27 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
151
153
|
body.endAt = end_at;
|
|
152
154
|
if (timezone !== undefined)
|
|
153
155
|
body.timezone = timezone;
|
|
156
|
+
if (name !== undefined)
|
|
157
|
+
body.name = name;
|
|
158
|
+
if (description !== undefined)
|
|
159
|
+
body.description = description;
|
|
154
160
|
return toolResult(await (mutationClient ?? getClient()).put(`/api/campaign/${encodeURIComponent(id)}/configuration`, body));
|
|
155
161
|
}
|
|
156
162
|
catch (error) {
|
|
157
163
|
return handleToolError(error);
|
|
158
164
|
}
|
|
159
165
|
});
|
|
166
|
+
server.tool("delete_campaign", "Irreversibly delete one OPEN campaign that is DRAFT or PAUSED. Active and permanently closed campaigns are refused. This operation never sends outreach. Always confirm with the user before calling this tool.", {
|
|
167
|
+
id: z.string().describe("Campaign ID."),
|
|
168
|
+
confirmation: z.literal("delete_campaign").describe("Exact acknowledgement required before deleting the campaign."),
|
|
169
|
+
}, async ({ id }) => {
|
|
170
|
+
try {
|
|
171
|
+
return toolResult(await (deleteClient ?? getClient()).delete(`/api/campaign/${encodeURIComponent(id)}`));
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
return handleToolError(error);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
160
177
|
// ── get_campaign ───────────────────────────────────────────────────────
|
|
161
178
|
server.tool("get_campaign", "Retrieve a campaign chosen from list_campaigns, scoped to the explicitly selected " +
|
|
162
179
|
"organization. Includes status, effectiveState, pauseReason, pausedAt, window, terminal " +
|
|
@@ -262,6 +279,51 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
262
279
|
return handleToolError(error);
|
|
263
280
|
}
|
|
264
281
|
});
|
|
282
|
+
server.tool("get_campaign_audience", "Read only the explicit audience of a campaign in an explicitly selected organization. " +
|
|
283
|
+
"Returns decision-safe presence signals, never outreach message content, and never sends.", {
|
|
284
|
+
organization_id: z.string().describe("Organization selected explicitly via list_organizations."),
|
|
285
|
+
campaign_id: z.string().describe("Campaign ID selected from list_campaigns or get_campaign."),
|
|
286
|
+
}, async ({ organization_id, campaign_id }) => {
|
|
287
|
+
try {
|
|
288
|
+
return toolResult(await (readClient ?? getClient()).get(`/api/campaign/${encodeURIComponent(campaign_id)}/audience`, new URLSearchParams({ organizationId: organization_id })));
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
return handleToolError(error);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
server.tool("preview_unenroll_campaign_audience", "Deterministically preview explicit campaign members that can be removed because the campaign-channel message is missing or no delivery channel exists. " +
|
|
295
|
+
"Read-only, no send, and returns the exact lead_ids required by unenroll_campaign_audience.", {
|
|
296
|
+
organization_id: z.string().describe("Organization selected explicitly via list_organizations."),
|
|
297
|
+
campaign_id: z.string().describe("Campaign ID selected from list_campaigns or get_campaign."),
|
|
298
|
+
}, async ({ organization_id, campaign_id }) => {
|
|
299
|
+
try {
|
|
300
|
+
const data = await (readClient ?? getClient()).get(`/api/campaign/${encodeURIComponent(campaign_id)}/audience`, new URLSearchParams({ organizationId: organization_id }));
|
|
301
|
+
const leadIds = Array.isArray(data.removableLeadIds) ? data.removableLeadIds : [];
|
|
302
|
+
return toolResult({ ...data, leadIds, noSend: true, persisted: false });
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
return handleToolError(error);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
server.tool("unenroll_campaign_audience", "Remove exactly the lead_ids returned by preview_unenroll_campaign_audience. Re-reads the preview first and refuses a stale, partial, or expanded selection. " +
|
|
309
|
+
"This only removes explicit membership; it never sends. An ACTIVE campaign may be replanned by Leadify solely to stop pending jobs.", {
|
|
310
|
+
organization_id: z.string().describe("Organization selected explicitly via list_organizations."),
|
|
311
|
+
campaign_id: z.string().describe("Campaign ID selected from preview_unenroll_campaign_audience."),
|
|
312
|
+
lead_ids: z.array(z.string()).min(1).describe("Exact, complete lead_ids returned by the preview."),
|
|
313
|
+
}, async ({ organization_id, campaign_id, lead_ids }) => {
|
|
314
|
+
try {
|
|
315
|
+
const data = await (readClient ?? getClient()).get(`/api/campaign/${encodeURIComponent(campaign_id)}/audience`, new URLSearchParams({ organizationId: organization_id }));
|
|
316
|
+
const expected = Array.isArray(data.removableLeadIds) ? data.removableLeadIds : [];
|
|
317
|
+
const selected = [...new Set(lead_ids)].sort();
|
|
318
|
+
if (selected.length !== lead_ids.length || JSON.stringify(selected) !== JSON.stringify([...expected].sort())) {
|
|
319
|
+
throw new Error("lead_ids must exactly match the current preview_unenroll_campaign_audience result");
|
|
320
|
+
}
|
|
321
|
+
return toolResult(await (deleteClient ?? getClient()).delete(`/api/campaign/${encodeURIComponent(campaign_id)}/prospects`, { leadIds: lead_ids }));
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
return handleToolError(error);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
265
327
|
// ── export_campaign ────────────────────────────────────────────────────
|
|
266
328
|
server.tool("export_campaign", "Export a campaign's statistics and metrics as CSV. A permanently closed campaign is exported " +
|
|
267
329
|
"from its immutable final report, so late replies remain visible in current activity without " +
|
|
@@ -161,6 +161,13 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
161
161
|
implication?: string | undefined;
|
|
162
162
|
}>, "many">>;
|
|
163
163
|
}, "strict", z.ZodTypeAny, {
|
|
164
|
+
painPoints?: {
|
|
165
|
+
id: string;
|
|
166
|
+
title: string;
|
|
167
|
+
description?: string | undefined;
|
|
168
|
+
customerCaseIds?: string[] | undefined;
|
|
169
|
+
useCaseIds?: string[] | undefined;
|
|
170
|
+
}[] | undefined;
|
|
164
171
|
allowedVocabulary?: string[] | undefined;
|
|
165
172
|
prohibitedVocabulary?: string[] | undefined;
|
|
166
173
|
legalConstraints?: {
|
|
@@ -191,13 +198,6 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
191
198
|
description?: string | undefined;
|
|
192
199
|
customerCaseIds?: string[] | undefined;
|
|
193
200
|
}[] | undefined;
|
|
194
|
-
painPoints?: {
|
|
195
|
-
id: string;
|
|
196
|
-
title: string;
|
|
197
|
-
description?: string | undefined;
|
|
198
|
-
customerCaseIds?: string[] | undefined;
|
|
199
|
-
useCaseIds?: string[] | undefined;
|
|
200
|
-
}[] | undefined;
|
|
201
201
|
qualificationSignals?: {
|
|
202
202
|
id: string;
|
|
203
203
|
title: string;
|
|
@@ -226,6 +226,13 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
226
226
|
implication?: string | undefined;
|
|
227
227
|
}[] | undefined;
|
|
228
228
|
}, {
|
|
229
|
+
painPoints?: {
|
|
230
|
+
id: string;
|
|
231
|
+
title: string;
|
|
232
|
+
description?: string | undefined;
|
|
233
|
+
customerCaseIds?: string[] | undefined;
|
|
234
|
+
useCaseIds?: string[] | undefined;
|
|
235
|
+
}[] | undefined;
|
|
229
236
|
allowedVocabulary?: string[] | undefined;
|
|
230
237
|
prohibitedVocabulary?: string[] | undefined;
|
|
231
238
|
legalConstraints?: {
|
|
@@ -256,13 +263,6 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
256
263
|
description?: string | undefined;
|
|
257
264
|
customerCaseIds?: string[] | undefined;
|
|
258
265
|
}[] | undefined;
|
|
259
|
-
painPoints?: {
|
|
260
|
-
id: string;
|
|
261
|
-
title: string;
|
|
262
|
-
description?: string | undefined;
|
|
263
|
-
customerCaseIds?: string[] | undefined;
|
|
264
|
-
useCaseIds?: string[] | undefined;
|
|
265
|
-
}[] | undefined;
|
|
266
266
|
qualificationSignals?: {
|
|
267
267
|
id: string;
|
|
268
268
|
title: string;
|
|
@@ -291,6 +291,13 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
291
291
|
implication?: string | undefined;
|
|
292
292
|
}[] | undefined;
|
|
293
293
|
}>, {
|
|
294
|
+
painPoints?: {
|
|
295
|
+
id: string;
|
|
296
|
+
title: string;
|
|
297
|
+
description?: string | undefined;
|
|
298
|
+
customerCaseIds?: string[] | undefined;
|
|
299
|
+
useCaseIds?: string[] | undefined;
|
|
300
|
+
}[] | undefined;
|
|
294
301
|
allowedVocabulary?: string[] | undefined;
|
|
295
302
|
prohibitedVocabulary?: string[] | undefined;
|
|
296
303
|
legalConstraints?: {
|
|
@@ -321,13 +328,6 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
321
328
|
description?: string | undefined;
|
|
322
329
|
customerCaseIds?: string[] | undefined;
|
|
323
330
|
}[] | undefined;
|
|
324
|
-
painPoints?: {
|
|
325
|
-
id: string;
|
|
326
|
-
title: string;
|
|
327
|
-
description?: string | undefined;
|
|
328
|
-
customerCaseIds?: string[] | undefined;
|
|
329
|
-
useCaseIds?: string[] | undefined;
|
|
330
|
-
}[] | undefined;
|
|
331
331
|
qualificationSignals?: {
|
|
332
332
|
id: string;
|
|
333
333
|
title: string;
|
|
@@ -356,6 +356,13 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
356
356
|
implication?: string | undefined;
|
|
357
357
|
}[] | undefined;
|
|
358
358
|
}, {
|
|
359
|
+
painPoints?: {
|
|
360
|
+
id: string;
|
|
361
|
+
title: string;
|
|
362
|
+
description?: string | undefined;
|
|
363
|
+
customerCaseIds?: string[] | undefined;
|
|
364
|
+
useCaseIds?: string[] | undefined;
|
|
365
|
+
}[] | undefined;
|
|
359
366
|
allowedVocabulary?: string[] | undefined;
|
|
360
367
|
prohibitedVocabulary?: string[] | undefined;
|
|
361
368
|
legalConstraints?: {
|
|
@@ -386,13 +393,6 @@ export declare const verticalContextContentSchema: z.ZodEffects<z.ZodObject<{
|
|
|
386
393
|
description?: string | undefined;
|
|
387
394
|
customerCaseIds?: string[] | undefined;
|
|
388
395
|
}[] | undefined;
|
|
389
|
-
painPoints?: {
|
|
390
|
-
id: string;
|
|
391
|
-
title: string;
|
|
392
|
-
description?: string | undefined;
|
|
393
|
-
customerCaseIds?: string[] | undefined;
|
|
394
|
-
useCaseIds?: string[] | undefined;
|
|
395
|
-
}[] | undefined;
|
|
396
396
|
qualificationSignals?: {
|
|
397
397
|
id: string;
|
|
398
398
|
title: string;
|
package/dist/tools/leads.js
CHANGED
|
@@ -285,6 +285,22 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
285
285
|
return handleToolError(error);
|
|
286
286
|
}
|
|
287
287
|
});
|
|
288
|
+
server.tool("clear_lead_field", "Irreversibly remove one stored value from a lead without changing its lead-group schema. " +
|
|
289
|
+
"Canonical relationship fields require their dedicated relationship mutation. Always confirm with the user before calling this tool.", {
|
|
290
|
+
lead_id: z.string().describe("ID of the lead to change."),
|
|
291
|
+
property_name: z.string().min(1).describe("Stored field name to clear. The field definition remains in the schema."),
|
|
292
|
+
confirmation: z.literal("clear_lead_field").describe("Exact acknowledgement required before removing the stored value."),
|
|
293
|
+
}, async ({ lead_id, property_name }) => {
|
|
294
|
+
try {
|
|
295
|
+
return toolResult(await client.delete("/clear-lead-field", {
|
|
296
|
+
leadId: lead_id,
|
|
297
|
+
propertyName: property_name,
|
|
298
|
+
}));
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
return handleToolError(error);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
288
304
|
// ── delete_sequence_messages ──────────────────────────────────────────
|
|
289
305
|
server.tool("upsert_lead_note", "Create a free-form note in a prospect's Notes tab. Idempotent for the same lead and exact note body: retrying the call returns the original note instead of adding a duplicate.", {
|
|
290
306
|
lead_id: z.string().describe("ID of the prospect that owns the note."),
|
package/dist/tools/personas.js
CHANGED
|
@@ -6,9 +6,68 @@ import { LeadifyApiError, handleToolError, toolResult } from "../types.js";
|
|
|
6
6
|
* versioned `personaContract`. These tools deliberately do not expose any
|
|
7
7
|
* historical Persona field or field-specific patch operation.
|
|
8
8
|
*/
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
.
|
|
9
|
+
const tierSchema = z.object({
|
|
10
|
+
rule: z.string(),
|
|
11
|
+
expectedEvidence: z.string(),
|
|
12
|
+
examples: z.array(z.string()).max(20),
|
|
13
|
+
}).strict();
|
|
14
|
+
const commonContractSchema = z.object({
|
|
15
|
+
schemaVersion: z.literal(2),
|
|
16
|
+
kind: z.enum(["PERSON", "COMPANY"]),
|
|
17
|
+
targeting: z.object({
|
|
18
|
+
targetProfile: z.unknown().nullable(),
|
|
19
|
+
disqualificationCriteria: z.unknown().nullable(),
|
|
20
|
+
schemaPacks: z.array(z.string()).max(2),
|
|
21
|
+
agentDomain: z.string().nullable(),
|
|
22
|
+
outputLanguage: z.string().nullable(),
|
|
23
|
+
}).strict(),
|
|
24
|
+
qualification: z.object({
|
|
25
|
+
tiers: z.object({
|
|
26
|
+
hot: tierSchema,
|
|
27
|
+
warm: tierSchema,
|
|
28
|
+
cold: tierSchema,
|
|
29
|
+
disqualified: tierSchema,
|
|
30
|
+
}).strict(),
|
|
31
|
+
signalExamples: z.array(z.object({
|
|
32
|
+
id: z.string().min(1),
|
|
33
|
+
signal: z.string().min(1),
|
|
34
|
+
level: z.enum(["GOLDEN", "CRITICAL", "STRONG", "INFO"]),
|
|
35
|
+
justification: z.string().min(1),
|
|
36
|
+
}).strict()).max(100),
|
|
37
|
+
signalTierRules: z.unknown().nullable(),
|
|
38
|
+
enableSignals: z.boolean(),
|
|
39
|
+
}).strict(),
|
|
40
|
+
intelligence: z.object({
|
|
41
|
+
activeTools: z.array(z.string()),
|
|
42
|
+
painPoints: z.unknown().nullable(),
|
|
43
|
+
icpStrategy: z.unknown().nullable(),
|
|
44
|
+
cardAnalysisSections: z.unknown().nullable(),
|
|
45
|
+
}).strict(),
|
|
46
|
+
outreach: z.object({
|
|
47
|
+
outreachSignalRouting: z.unknown().nullable(),
|
|
48
|
+
outreachTemplates: z.unknown().nullable(),
|
|
49
|
+
toneInstructions: z.string().nullable(),
|
|
50
|
+
outreachFields: z.unknown().nullable(),
|
|
51
|
+
}).strict(),
|
|
52
|
+
}).strict();
|
|
53
|
+
const personExtensionSchema = z.object({
|
|
54
|
+
assessmentAxes: z.object({
|
|
55
|
+
identityAndEmployment: z.object({ question: z.string(), expectedEvidence: z.string() }).strict(),
|
|
56
|
+
commercialLink: z.object({ question: z.string(), expectedEvidence: z.string() }).strict(),
|
|
57
|
+
actionPower: z.object({ question: z.string(), expectedEvidence: z.string() }).strict(),
|
|
58
|
+
individualMoment: z.object({ question: z.string(), expectedEvidence: z.string() }).strict(),
|
|
59
|
+
}).strict(),
|
|
60
|
+
roleArchetypes: z.array(z.object({
|
|
61
|
+
name: z.string().min(1),
|
|
62
|
+
relevanceRule: z.string(),
|
|
63
|
+
expectedEvidence: z.string(),
|
|
64
|
+
examples: z.array(z.string()).max(20),
|
|
65
|
+
}).strict()).max(50),
|
|
66
|
+
}).strict();
|
|
67
|
+
const contractSchema = z.discriminatedUnion("kind", [
|
|
68
|
+
commonContractSchema.extend({ kind: z.literal("COMPANY"), person: z.null() }).strict(),
|
|
69
|
+
commonContractSchema.extend({ kind: z.literal("PERSON"), person: personExtensionSchema }).strict(),
|
|
70
|
+
]).describe("Complete strict Persona contract, schemaVersion 2.");
|
|
12
71
|
const tenantScope = {
|
|
13
72
|
organization_id: z
|
|
14
73
|
.string()
|
|
@@ -27,6 +86,7 @@ function canonicalPersona(persona) {
|
|
|
27
86
|
if (!persona.personaContract || typeof persona.personaContract !== "object") {
|
|
28
87
|
throw new Error("PERSONA_CONTRACT_REQUIRED: run the audited Persona migration before using this MCP.");
|
|
29
88
|
}
|
|
89
|
+
const contract = contractSchema.parse(persona.personaContract);
|
|
30
90
|
return {
|
|
31
91
|
id: persona.id,
|
|
32
92
|
name: persona.name,
|
|
@@ -34,7 +94,7 @@ function canonicalPersona(persona) {
|
|
|
34
94
|
updatedAt: persona.updatedAt,
|
|
35
95
|
revision: persona.revision ?? 0,
|
|
36
96
|
status: persona.status,
|
|
37
|
-
contract
|
|
97
|
+
contract,
|
|
38
98
|
};
|
|
39
99
|
}
|
|
40
100
|
async function fetchPersona(id, organizationId, client) {
|
|
@@ -155,7 +215,7 @@ export function registerPersonaTools(server, injectedClient) {
|
|
|
155
215
|
organizationId: organization_id,
|
|
156
216
|
id,
|
|
157
217
|
expectedUpdatedAt: expected_updated_at,
|
|
158
|
-
contract: setContractPath(contract, path, value),
|
|
218
|
+
contract: contractSchema.parse(setContractPath(contract, path, value)),
|
|
159
219
|
client,
|
|
160
220
|
}));
|
|
161
221
|
}
|
package/dist/tools/schema.js
CHANGED
|
@@ -125,7 +125,8 @@ export function registerSchemaTools(server, client) {
|
|
|
125
125
|
.array(z.string())
|
|
126
126
|
.min(1)
|
|
127
127
|
.describe("Array of column/field names to delete."),
|
|
128
|
-
|
|
128
|
+
force: z.literal(true).optional().describe("Required only to remove a rogue data key that is absent from the group schema."),
|
|
129
|
+
}, async ({ lead_group_id, column_names, force }) => {
|
|
129
130
|
try {
|
|
130
131
|
const body = {
|
|
131
132
|
leadGroupId: lead_group_id,
|
|
@@ -136,6 +137,8 @@ export function registerSchemaTools(server, client) {
|
|
|
136
137
|
else {
|
|
137
138
|
body.columnNames = column_names;
|
|
138
139
|
}
|
|
140
|
+
if (force)
|
|
141
|
+
body.force = true;
|
|
139
142
|
const data = await (client ?? getClient()).post("/delete-column", body);
|
|
140
143
|
return toolResult(data);
|
|
141
144
|
}
|