@agifyai/leadify-mcp 8.7.0 → 8.7.1
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 +4 -4
- package/dist/tools/personas.js +72 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -300,10 +300,10 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
300
300
|
| `apply_canonical_identity_backfill` | Appliquer exactement un backfill d'identité relu par digest, sans outreach ni activation. |
|
|
301
301
|
| `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. |
|
|
302
302
|
| `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. |
|
|
303
|
-
| `list_persona_contracts` / `get_persona_contract` | Lister ou lire les contrats Persona canoniques d’une organisation explicitement sélectionnée. |
|
|
304
|
-
| `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. |
|
|
305
|
-
| `replace_persona_contract` / `patch_persona_contract` | Remplacer ou modifier un contrat canonique v2 avec verrou optimiste.
|
|
306
|
-
| `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. |
|
|
303
|
+
| `list_persona_contracts` / `get_persona_contract` | Lister ou lire les contrats Persona canoniques v2 d’une organisation explicitement sélectionnée. `intelligence` ne contient que `painPoints`, `icpStrategy` et `cardAnalysisSections` ; `activeTools` n’appartient pas au contrat. |
|
|
304
|
+
| `create_persona_contract` | Créer un Persona tenant-scoped et lié à une Verticale depuis un contrat canonique v2 complet et strict. `tool_pack_id` est requis à la création et reste hors contrat. Toute clé inconnue est refusée. |
|
|
305
|
+
| `replace_persona_contract` / `patch_persona_contract` | Remplacer ou modifier un contrat canonique v2 avec verrou optimiste. `name` / `description` sont acceptés sur replace. `tool_pack_id` est optionnel : s’il est omis, le pack persisté est réutilisé. Une Persona ACTIVE mutée repasse en DRAFT. |
|
|
306
|
+
| `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. Après un replace/patch, rappeler ce tool vers `ACTIVE` pour que les groupes résolvent à nouveau la Persona. |
|
|
307
307
|
| `list_data_sources` | Lister toutes les sources de données configurées (par pays puis nom). |
|
|
308
308
|
| `create_data_source` | Créer une nouvelle source de données (admin uniquement). |
|
|
309
309
|
| `update_data_source` | Modifier une source de données existante (admin uniquement). |
|
package/dist/tools/personas.js
CHANGED
|
@@ -5,6 +5,10 @@ import { LeadifyApiError, handleToolError, toolResult } from "../types.js";
|
|
|
5
5
|
* PRD-1423 hard cutover: Persona business content is represented only by the
|
|
6
6
|
* versioned `personaContract`. These tools deliberately do not expose any
|
|
7
7
|
* historical Persona field or field-specific patch operation.
|
|
8
|
+
*
|
|
9
|
+
* The published MCP contract must match the server `personaContractSchema`.
|
|
10
|
+
* `intelligence.activeTools` is not part of that document; the tool pack lives
|
|
11
|
+
* on `toolPackId`, outside the contract.
|
|
8
12
|
*/
|
|
9
13
|
const tierSchema = z.object({
|
|
10
14
|
rule: z.string(),
|
|
@@ -38,7 +42,6 @@ const commonContractSchema = z.object({
|
|
|
38
42
|
enableSignals: z.boolean(),
|
|
39
43
|
}).strict(),
|
|
40
44
|
intelligence: z.object({
|
|
41
|
-
activeTools: z.array(z.string()),
|
|
42
45
|
painPoints: z.unknown().nullable(),
|
|
43
46
|
icpStrategy: z.unknown().nullable(),
|
|
44
47
|
cardAnalysisSections: z.unknown().nullable(),
|
|
@@ -67,7 +70,24 @@ const personExtensionSchema = z.object({
|
|
|
67
70
|
const contractSchema = z.discriminatedUnion("kind", [
|
|
68
71
|
commonContractSchema.extend({ kind: z.literal("COMPANY"), person: z.null() }).strict(),
|
|
69
72
|
commonContractSchema.extend({ kind: z.literal("PERSON"), person: personExtensionSchema }).strict(),
|
|
70
|
-
])
|
|
73
|
+
]);
|
|
74
|
+
function withoutOutOfContractKeys(value) {
|
|
75
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
76
|
+
return value;
|
|
77
|
+
const contract = structuredClone(value);
|
|
78
|
+
const intelligence = contract.intelligence;
|
|
79
|
+
if (intelligence && typeof intelligence === "object" && !Array.isArray(intelligence)) {
|
|
80
|
+
delete intelligence.activeTools;
|
|
81
|
+
}
|
|
82
|
+
return contract;
|
|
83
|
+
}
|
|
84
|
+
function parsePersistedContract(value) {
|
|
85
|
+
return contractSchema.parse(withoutOutOfContractKeys(value));
|
|
86
|
+
}
|
|
87
|
+
function parseWrittenContract(value) {
|
|
88
|
+
return parsePersistedContract(value);
|
|
89
|
+
}
|
|
90
|
+
const contractInputSchema = z.preprocess(withoutOutOfContractKeys, contractSchema).describe("Complete strict Persona contract, schemaVersion 2. intelligence.activeTools is not a contract field and is stripped if sent.");
|
|
71
91
|
const tenantScope = {
|
|
72
92
|
organization_id: z
|
|
73
93
|
.string()
|
|
@@ -82,15 +102,23 @@ const optimisticScope = {
|
|
|
82
102
|
.datetime({ offset: true })
|
|
83
103
|
.describe("Exact updatedAt obtained from a preceding read."),
|
|
84
104
|
};
|
|
105
|
+
function requireToolPackId(persona, override) {
|
|
106
|
+
const toolPackId = override?.trim() || persona.toolPackId?.trim();
|
|
107
|
+
if (!toolPackId)
|
|
108
|
+
throw new Error("PERSONA_TOOL_PACK_REQUIRED");
|
|
109
|
+
return toolPackId;
|
|
110
|
+
}
|
|
85
111
|
function canonicalPersona(persona) {
|
|
86
112
|
if (!persona.personaContract || typeof persona.personaContract !== "object") {
|
|
87
113
|
throw new Error("PERSONA_CONTRACT_REQUIRED: run the audited Persona migration before using this MCP.");
|
|
88
114
|
}
|
|
89
|
-
const contract =
|
|
115
|
+
const contract = parsePersistedContract(persona.personaContract);
|
|
90
116
|
return {
|
|
91
117
|
id: persona.id,
|
|
92
118
|
name: persona.name,
|
|
119
|
+
description: persona.description ?? null,
|
|
93
120
|
verticalId: persona.verticalId,
|
|
121
|
+
toolPackId: persona.toolPackId ?? null,
|
|
94
122
|
updatedAt: persona.updatedAt,
|
|
95
123
|
revision: persona.revision ?? 0,
|
|
96
124
|
status: persona.status,
|
|
@@ -139,11 +167,13 @@ async function replaceContract(input) {
|
|
|
139
167
|
}
|
|
140
168
|
return input.client.post("/api/persona", {
|
|
141
169
|
id: persona.id,
|
|
142
|
-
name: persona.name,
|
|
170
|
+
name: input.name ?? persona.name,
|
|
143
171
|
organizationId: input.organizationId,
|
|
144
172
|
verticalId: persona.verticalId,
|
|
145
173
|
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
146
|
-
|
|
174
|
+
toolPackId: requireToolPackId(persona, input.toolPackId),
|
|
175
|
+
...(input.description !== undefined ? { description: input.description } : {}),
|
|
176
|
+
personaContract: parseWrittenContract(input.contract),
|
|
147
177
|
});
|
|
148
178
|
}
|
|
149
179
|
export function registerPersonaTools(server, injectedClient) {
|
|
@@ -168,33 +198,54 @@ export function registerPersonaTools(server, injectedClient) {
|
|
|
168
198
|
return handleToolError(error);
|
|
169
199
|
}
|
|
170
200
|
});
|
|
171
|
-
server.tool("create_persona_contract", "Create a Persona from one complete canonical contract.
|
|
201
|
+
server.tool("create_persona_contract", "Create a Persona from one complete canonical contract v2. intelligence.activeTools is not part of the contract. tool_pack_id is required on create and lives outside the contract.", {
|
|
172
202
|
...tenantScope,
|
|
173
203
|
vertical_id: z.string().trim().min(1),
|
|
174
204
|
name: z.string().trim().min(1),
|
|
175
205
|
description: z.string().optional(),
|
|
176
|
-
|
|
177
|
-
|
|
206
|
+
tool_pack_id: z
|
|
207
|
+
.string()
|
|
208
|
+
.trim()
|
|
209
|
+
.min(1)
|
|
210
|
+
.describe("Tenant-scoped tool pack. Not part of personaContract."),
|
|
211
|
+
contract: contractInputSchema,
|
|
212
|
+
}, async ({ organization_id, vertical_id, name, description, tool_pack_id, contract }) => {
|
|
178
213
|
try {
|
|
179
214
|
return toolResult(await client.post("/api/persona", {
|
|
180
215
|
organizationId: organization_id,
|
|
181
216
|
verticalId: vertical_id,
|
|
182
217
|
name,
|
|
218
|
+
toolPackId: tool_pack_id,
|
|
183
219
|
...(description !== undefined ? { description } : {}),
|
|
184
|
-
personaContract: contract,
|
|
220
|
+
personaContract: parseWrittenContract(contract),
|
|
185
221
|
}));
|
|
186
222
|
}
|
|
187
223
|
catch (error) {
|
|
188
224
|
return handleToolError(error);
|
|
189
225
|
}
|
|
190
226
|
});
|
|
191
|
-
server.tool("replace_persona_contract", "Replace the complete canonical contract using optimistic concurrency.
|
|
227
|
+
server.tool("replace_persona_contract", "Replace the complete canonical contract using optimistic concurrency. Omitting tool_pack_id reuses the persisted pack. Mutating an ACTIVE Persona returns it to DRAFT; call change_persona_status to reactivate.", {
|
|
228
|
+
...optimisticScope,
|
|
229
|
+
id: z.string().min(1),
|
|
230
|
+
name: z.string().trim().min(1).optional(),
|
|
231
|
+
description: z.string().optional(),
|
|
232
|
+
tool_pack_id: z
|
|
233
|
+
.string()
|
|
234
|
+
.trim()
|
|
235
|
+
.min(1)
|
|
236
|
+
.optional()
|
|
237
|
+
.describe("Optional pack change. When omitted, the persisted toolPackId is reused."),
|
|
238
|
+
contract: contractInputSchema,
|
|
239
|
+
}, async ({ organization_id, id, expected_updated_at, name, description, tool_pack_id, contract }) => {
|
|
192
240
|
try {
|
|
193
241
|
return toolResult(await replaceContract({
|
|
194
242
|
organizationId: organization_id,
|
|
195
243
|
id,
|
|
196
244
|
expectedUpdatedAt: expected_updated_at,
|
|
197
|
-
|
|
245
|
+
name,
|
|
246
|
+
description,
|
|
247
|
+
toolPackId: tool_pack_id,
|
|
248
|
+
contract: parseWrittenContract(contract),
|
|
198
249
|
client,
|
|
199
250
|
}));
|
|
200
251
|
}
|
|
@@ -202,12 +253,18 @@ export function registerPersonaTools(server, injectedClient) {
|
|
|
202
253
|
return handleToolError(error);
|
|
203
254
|
}
|
|
204
255
|
});
|
|
205
|
-
server.tool("patch_persona_contract", "Change one canonical contract path using optimistic concurrency. Removing a field is forbidden because the contract is strict and complete.", {
|
|
256
|
+
server.tool("patch_persona_contract", "Change one canonical contract path using optimistic concurrency. Removing a field is forbidden because the contract is strict and complete. intelligence.activeTools is not a contract path. Mutating an ACTIVE Persona returns it to DRAFT; call change_persona_status to reactivate.", {
|
|
206
257
|
...optimisticScope,
|
|
207
258
|
id: z.string().min(1),
|
|
208
259
|
path: z.string().trim().min(1),
|
|
209
260
|
value: z.unknown(),
|
|
210
|
-
|
|
261
|
+
tool_pack_id: z
|
|
262
|
+
.string()
|
|
263
|
+
.trim()
|
|
264
|
+
.min(1)
|
|
265
|
+
.optional()
|
|
266
|
+
.describe("Optional pack change. When omitted, the persisted toolPackId is reused."),
|
|
267
|
+
}, async ({ organization_id, id, expected_updated_at, path, value, tool_pack_id }) => {
|
|
211
268
|
try {
|
|
212
269
|
const persona = await fetchPersona(id, organization_id, client);
|
|
213
270
|
const contract = canonicalPersona(persona).contract;
|
|
@@ -215,6 +272,7 @@ export function registerPersonaTools(server, injectedClient) {
|
|
|
215
272
|
organizationId: organization_id,
|
|
216
273
|
id,
|
|
217
274
|
expectedUpdatedAt: expected_updated_at,
|
|
275
|
+
toolPackId: tool_pack_id,
|
|
218
276
|
contract: contractSchema.parse(setContractPath(contract, path, value)),
|
|
219
277
|
client,
|
|
220
278
|
}));
|
|
@@ -223,7 +281,7 @@ export function registerPersonaTools(server, injectedClient) {
|
|
|
223
281
|
return handleToolError(error);
|
|
224
282
|
}
|
|
225
283
|
});
|
|
226
|
-
server.tool("change_persona_status", "Change only Persona lifecycle status; it never changes business contract content.", { ...optimisticScope, id: z.string().min(1), status: z.enum(["DRAFT", "ACTIVE", "ARCHIVED"]) }, async ({ organization_id, id, expected_updated_at, status }) => {
|
|
284
|
+
server.tool("change_persona_status", "Change only Persona lifecycle status; it never changes business contract content. After replace/patch, an ACTIVE Persona is typically returned to DRAFT and must be set back to ACTIVE for groups to resolve it again.", { ...optimisticScope, id: z.string().min(1), status: z.enum(["DRAFT", "ACTIVE", "ARCHIVED"]) }, async ({ organization_id, id, expected_updated_at, status }) => {
|
|
227
285
|
try {
|
|
228
286
|
return toolResult(await client.post(`/api/persona/${encodeURIComponent(id)}/status?organizationId=${encodeURIComponent(organization_id)}`, { expectedUpdatedAt: expected_updated_at, status }));
|
|
229
287
|
}
|