@agifyai/leadify-mcp 8.6.7 → 8.6.9

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 CHANGED
@@ -289,11 +289,14 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
289
289
  | `preview_canonical_relationship_migration` | Prévisualiser une migration de champs historiques, divergences et quarantaines comprises, sans mutation. |
290
290
  | `apply_canonical_relationship_migration` | Appliquer exactement un plan prévisualisé et borné grâce à son digest. |
291
291
  | `rollback_canonical_relationship_migration` | Tombstoner les liens créés par un plan de migration précis. |
292
+ | `configure_lead_group_relations` | Configurer, dans un tenant explicitement vérifié, la projection legacy relue par une migration canonique. |
293
+ | `preview_canonical_identity_backfill` | Prévisualiser le backfill d'identité tenant-scoped d'un groupe et de ses parents Company configurés. |
294
+ | `apply_canonical_identity_backfill` | Appliquer exactement un backfill d'identité relu par digest, sans outreach ni activation. |
292
295
  | `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. |
293
296
  | `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. |
294
297
  | `list_persona_contracts` / `get_persona_contract` | Lister ou lire les contrats Persona canoniques d’une organisation explicitement sélectionnée. |
295
- | `create_persona_contract` | Créer un Persona tenant-scoped et lié à une Verticale depuis un contrat canonique complet. |
296
- | `replace_persona_contract` / `patch_persona_contract` | Remplacer ou modifier un contrat canonique avec verrou optimiste ; aucune colonne Persona historique n’est exposée. |
298
+ | `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. |
299
+ | `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. |
297
300
  | `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. |
298
301
  | `list_data_sources` | Lister toutes les sources de données configurées (par pays puis nom). |
299
302
  | `create_data_source` | Créer une nouvelle source de données (admin uniquement). |
package/dist/server.js CHANGED
@@ -15,6 +15,7 @@ import { registerFineTuningTools } from "./tools/fine_tuning.js";
15
15
  import { registerContextWorkspaceTools } from "./tools/context_workspace.js";
16
16
  import { registerLeadViewTools } from "./tools/views.js";
17
17
  import { registerRelationshipTools } from "./tools/relationships.js";
18
+ import { registerCanonicalBackfillTools } from "./tools/canonical_backfill.js";
18
19
  import { registerCommercialTruthTools } from "./tools/commercial_truth.js";
19
20
  import { registerEventTools } from "./tools/events.js";
20
21
  import { registerContextEntityTools } from "./tools/context_entities.js";
@@ -30,6 +31,7 @@ export function createServer() {
30
31
  registerLeadViewTools(server);
31
32
  registerLeadTools(server);
32
33
  registerRelationshipTools(server);
34
+ registerCanonicalBackfillTools(server);
33
35
  registerEventTools(server);
34
36
  registerCommercialTruthTools(server);
35
37
  registerSchemaTools(server);
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCanonicalBackfillTools(server: McpServer, client?: import("../client.js").LeadifyClient): void;
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ import { getClient } from "../client.js";
3
+ import { handleToolError, toolResult } from "../types.js";
4
+ const relationshipType = z.enum(["EMPLOYED_BY", "AFFILIATED_WITH", "PART_OF", "COLLEAGUE_OF"]);
5
+ const relation = z.object({
6
+ field: z.string().trim().min(1),
7
+ lead_group_id: z.string().trim().min(1),
8
+ target_field: z.string().trim().min(1).optional(),
9
+ display_field: z.string().trim().min(1).optional(),
10
+ inverse_field: z.string().trim().min(1).optional(),
11
+ canonical_type: relationshipType.optional(),
12
+ canonical_endpoint_role: z.enum(["SOURCE", "TARGET"]).optional(),
13
+ canonical_label: z.string().trim().min(1).max(120).optional(),
14
+ }).strict().superRefine((value, context) => {
15
+ if (Boolean(value.canonical_type) === Boolean(value.canonical_endpoint_role))
16
+ return;
17
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ["canonical_type"], message: "canonical_type and canonical_endpoint_role must be provided together" });
18
+ });
19
+ function apiRelation(value) {
20
+ return {
21
+ field: value.field,
22
+ leadGroupId: value.lead_group_id,
23
+ targetField: value.target_field,
24
+ displayField: value.display_field,
25
+ inverseField: value.inverse_field,
26
+ canonicalType: value.canonical_type,
27
+ canonicalEndpointRole: value.canonical_endpoint_role,
28
+ canonicalLabel: value.canonical_label,
29
+ };
30
+ }
31
+ const backfillScope = {
32
+ organization_id: z.string().trim().min(1),
33
+ lead_group_id: z.string().trim().min(1),
34
+ lead_ids: z.array(z.string().trim().min(1)).min(1).max(100),
35
+ };
36
+ export function registerCanonicalBackfillTools(server, client = getClient()) {
37
+ server.tool("configure_lead_group_relations", "Configure the legacy projection that a canonical migration reads. The organization is mandatory and verified against both groups; canonical_type and canonical_endpoint_role must be specified together. This changes no lead, identity, canonical relationship, campaign, or outreach.", { organization_id: z.string().trim().min(1), lead_group_id: z.string().trim().min(1), relations: z.array(relation).max(100) }, async ({ organization_id, lead_group_id, relations }) => {
38
+ try {
39
+ return toolResult(await client.put(`/api/lead-group/${encodeURIComponent(lead_group_id)}/relations`, {
40
+ organizationId: organization_id,
41
+ relations: relations.map(apiRelation),
42
+ }));
43
+ }
44
+ catch (error) {
45
+ return handleToolError(error);
46
+ }
47
+ });
48
+ server.tool("preview_canonical_identity_backfill", "Preview the tenant-scoped canonical identity backfill for one LeadGroup. It reads only the listed leads and their configured Company parent, returns an exact plan digest, and creates no identity, relationship, outreach, or activation.", backfillScope, async ({ organization_id, lead_group_id, lead_ids }) => {
49
+ try {
50
+ return toolResult(await client.post("/api/canonical-identity/backfill", {
51
+ organizationId: organization_id, leadGroupId: lead_group_id, leadIds: lead_ids,
52
+ projectionTarget: "lead", dryRun: true,
53
+ }));
54
+ }
55
+ catch (error) {
56
+ return handleToolError(error);
57
+ }
58
+ });
59
+ server.tool("apply_canonical_identity_backfill", "Apply exactly a reviewed canonical identity backfill plan. The organization and LeadGroup stay explicit, and the backend rejects a changed digest, cross-tenant lead, missing parent, or missing canonical Company account. It never sends outreach or activates campaigns.", { ...backfillScope, expected_plan_digest: z.string().regex(/^[a-f0-9]{64}$/) }, async ({ organization_id, lead_group_id, lead_ids, expected_plan_digest }) => {
60
+ try {
61
+ return toolResult(await client.post("/api/canonical-identity/backfill", {
62
+ organizationId: organization_id, leadGroupId: lead_group_id, leadIds: lead_ids,
63
+ projectionTarget: "lead", dryRun: false, expectedPlanDigest: expected_plan_digest,
64
+ }));
65
+ }
66
+ catch (error) {
67
+ return handleToolError(error);
68
+ }
69
+ });
70
+ }
@@ -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;
@@ -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 contractSchema = z
10
- .record(z.unknown())
11
- .describe("Complete versioned Persona contract. Leadify validates its exact schema.");
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: persona.personaContract,
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agifyai/leadify-mcp",
3
- "version": "8.6.7",
3
+ "version": "8.6.9",
4
4
  "description": "MCP server for Leadify lead management API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",