@agifyai/leadify-mcp 8.3.8 → 8.4.0
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 +10 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +3 -3
- package/dist/server.js +3 -1
- package/dist/tools/leads.js +5 -3
- package/dist/tools/personas.d.ts +6 -1
- package/dist/tools/personas.js +112 -36
- package/dist/tools/relationships.d.ts +2 -0
- package/dist/tools/relationships.js +124 -0
- package/dist/tools/schema.d.ts +4 -1
- package/dist/tools/schema.js +17 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -198,6 +198,7 @@ Conséquences pratiques :
|
|
|
198
198
|
| `update_schema` | Ajouter ou modifier les définitions de champs d'un groupe. |
|
|
199
199
|
| `delete_columns` | Supprimer des colonnes du schéma et des données d'un groupe. |
|
|
200
200
|
| `update_hidden_columns` | Afficher ou masquer des colonnes dans la vue tableau (réversible). |
|
|
201
|
+
| `list_crm_schema_packs` | Lister le catalogue canonique des CRM Schema Packs, leurs domaines et contraintes de compatibilité. |
|
|
201
202
|
| `add_campaign_log` | Enregistrer une entrée de log de campagne pour un lead. |
|
|
202
203
|
| `get_campaign_logs` | Récupérer les logs de campagne avec filtres et pagination. |
|
|
203
204
|
| `delete_campaign_log` | Supprimer une entrée de log de campagne. |
|
|
@@ -214,6 +215,13 @@ Conséquences pratiques :
|
|
|
214
215
|
| `add_activity` | Journaliser une interaction prospect (LinkedIn, email, call) dans le feed du lead. |
|
|
215
216
|
| `compile_context_pack` | Compiler en lecture seule un Context Pack canonique depuis une cible explicite (`organization`, `lead_group`, `campaign`, `account`, `person`) ou un couple lead + campagne pour `WRITE_OUTREACH`. Retourne publications courantes, sources, freshness, contradictions et provenance. N’utilise jamais la Data Room legacy ni ne choisit de cible implicite. |
|
|
216
217
|
| `get_context_workspace` | Lire le Context Workspace canonique versionné (`company_brain` ou `gtm_playbook`) d'une organisation explicitement sélectionnée. Retourne le brouillon et la dernière version publiée. |
|
|
218
|
+
| `list_canonical_relationships` | Lire les liens canoniques typés d'une organisation, dans les deux directions. |
|
|
219
|
+
| `create_canonical_relationship` | Créer un lien canonique tenant-scoped et idempotent entre deux identités fortes. |
|
|
220
|
+
| `update_canonical_relationship` | Modifier, restaurer ou tombstoner un lien via le ledger transactionnel partagé. |
|
|
221
|
+
| `tombstone_canonical_relationship` | Retirer réversiblement un lien sans supprimer son historique ni ses preuves. |
|
|
222
|
+
| `preview_canonical_relationship_migration` | Prévisualiser une migration de champs historiques, divergences et quarantaines comprises, sans mutation. |
|
|
223
|
+
| `apply_canonical_relationship_migration` | Appliquer exactement un plan prévisualisé et borné grâce à son digest. |
|
|
224
|
+
| `rollback_canonical_relationship_migration` | Tombstoner les liens créés par un plan de migration précis. |
|
|
217
225
|
| `update_company_brain_sections` | Modifier uniquement les sections indiquées du Company Brain : le MCP relit le brouillon ou la dernière publication, fusionne les sections ciblées, puis sauvegarde avec révision attendue et clé d'idempotence. |
|
|
218
226
|
| `update_gtm_playbook_sections` | Modifier uniquement les sections indiquées du GTM Playbook, avec la même lecture-fusion-révision atomiquement contrôlée. |
|
|
219
227
|
| `publish_context_workspace` | Publier une révision prête du Context Workspace après confirmation explicite (`confirm_publish: true`). Admin de l'organisation requis ; les raisons de non-readiness sont renvoyées par le serveur. |
|
|
@@ -225,7 +233,8 @@ Conséquences pratiques :
|
|
|
225
233
|
| `update_company_info_economics` | Patch du bloc economics (pricingModel, ticketRange, salesCycleMonths, triggers, defaultBaseline). |
|
|
226
234
|
| `update_company_info_product_icp` | Add / replace / remove un seul ICP dans productICPs[]. Lookup par productId (must match a products[].name). Cap 5. |
|
|
227
235
|
| `update_company_info_proof_wording` | Patch du bloc proofWording (keyMetrics, miniStories, forbiddenWords). |
|
|
228
|
-
| `upsert_persona` | Créer ou mettre à jour un
|
|
236
|
+
| `upsert_persona` | Créer ou mettre à jour atomiquement un Persona, avec `schema_packs` obligatoire à la création. |
|
|
237
|
+
| `update_persona_schema_packs` | Remplacer uniquement les CRM Schema Packs d’un Persona et relire les preuves de readiness des groupes liés. |
|
|
229
238
|
| `get_persona` | Récupérer un persona par son ID. |
|
|
230
239
|
| `get_lead_group_persona` | Récupérer le persona assigné à un groupe de leads. |
|
|
231
240
|
| `list_data_sources` | Lister toutes les sources de données configurées (par pays puis nom). |
|
package/dist/client.d.ts
CHANGED
|
@@ -7,6 +7,6 @@ export declare class LeadifyClient {
|
|
|
7
7
|
post(path: string, body: unknown): Promise<unknown>;
|
|
8
8
|
put(path: string, body: unknown): Promise<unknown>;
|
|
9
9
|
patch(path: string, body: unknown): Promise<unknown>;
|
|
10
|
-
delete(path: string): Promise<unknown>;
|
|
10
|
+
delete(path: string, body?: unknown): Promise<unknown>;
|
|
11
11
|
}
|
|
12
12
|
export declare function getClient(): LeadifyClient;
|
package/dist/client.js
CHANGED
|
@@ -17,7 +17,7 @@ export class LeadifyClient {
|
|
|
17
17
|
};
|
|
18
18
|
const init = { method, headers };
|
|
19
19
|
if (body !== undefined &&
|
|
20
|
-
(method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
20
|
+
(method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE")) {
|
|
21
21
|
headers["Content-Type"] = "application/json";
|
|
22
22
|
init.body = JSON.stringify(body);
|
|
23
23
|
}
|
|
@@ -53,9 +53,9 @@ export class LeadifyClient {
|
|
|
53
53
|
const url = new URL(path, this.baseUrl);
|
|
54
54
|
return this.request("PATCH", url, body);
|
|
55
55
|
}
|
|
56
|
-
async delete(path) {
|
|
56
|
+
async delete(path, body) {
|
|
57
57
|
const url = new URL(path, this.baseUrl);
|
|
58
|
-
return this.request("DELETE", url);
|
|
58
|
+
return this.request("DELETE", url, body);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
let _client = null;
|
package/dist/server.js
CHANGED
|
@@ -14,16 +14,18 @@ import { registerPipelineTools } from "./tools/pipeline.js";
|
|
|
14
14
|
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
|
+
import { registerRelationshipTools } from "./tools/relationships.js";
|
|
17
18
|
export function createServer() {
|
|
18
19
|
const server = new McpServer({
|
|
19
20
|
name: "leadify",
|
|
20
|
-
version: "8.
|
|
21
|
+
version: "8.4.0",
|
|
21
22
|
});
|
|
22
23
|
registerAuthTools(server);
|
|
23
24
|
registerOrganizationTools(server);
|
|
24
25
|
registerLeadGroupTools(server);
|
|
25
26
|
registerLeadViewTools(server);
|
|
26
27
|
registerLeadTools(server);
|
|
28
|
+
registerRelationshipTools(server);
|
|
27
29
|
registerSchemaTools(server);
|
|
28
30
|
registerLogTools(server);
|
|
29
31
|
registerCampaignTools(server);
|
package/dist/tools/leads.js
CHANGED
|
@@ -24,7 +24,8 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
24
24
|
server.tool("add_leads", "Add one or more leads to a specific lead group in Leadify. Provide the group ID " +
|
|
25
25
|
"and an array of lead objects with any combination of fields (email, firstName, " +
|
|
26
26
|
"lastName, company, jobTitle, phone, custom fields, card_* fields, percent_* " +
|
|
27
|
-
"fields,
|
|
27
|
+
"fields, etc.). Canonical relation fields are read-only here: use " +
|
|
28
|
+
"create_canonical_relationship or the migration tools instead. Optionally specify which fields should be " +
|
|
28
29
|
"treated as 'select' dropdowns in the UI. Returns the count of successfully " +
|
|
29
30
|
"imported leads and any errors. Use this for both bulk imports and single lead creation.", {
|
|
30
31
|
lead_group_id: z
|
|
@@ -35,7 +36,7 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
35
36
|
.describe("Array of lead objects. Each object is a key-value map of field names to values. " +
|
|
36
37
|
"Common fields: email, firstName, lastName, company, jobTitle, phone, linkedin, " +
|
|
37
38
|
"website, location, seniority, specialty, age. Also supports card_* fields, " +
|
|
38
|
-
"percent_* fields
|
|
39
|
+
"percent_* fields and boolean flags (decisionMaking, excluded). Canonical relation fields are rejected."),
|
|
39
40
|
is_select_fields: z
|
|
40
41
|
.array(z.string())
|
|
41
42
|
.optional()
|
|
@@ -149,7 +150,8 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
149
150
|
server.tool("update_lead", "Update one or more fields on an existing lead. Each update specifies the field " +
|
|
150
151
|
"name, new value, and optionally whether the field is a 'select' dropdown type. " +
|
|
151
152
|
"Supports all field types including special fields (card_*, percent_*, boolean flags " +
|
|
152
|
-
"like decisionMaking/excluded)
|
|
153
|
+
"like decisionMaking/excluded). When a configured canonical relation field is changed, " +
|
|
154
|
+
"the backend routes it through the typed relationship service and keeps the JSON field read-only. Returns the updated lead. " +
|
|
153
155
|
"For bulk updates across many leads, call this tool once per lead.", {
|
|
154
156
|
lead_id: z.string().describe("ID of the lead to update."),
|
|
155
157
|
updates: z
|
package/dist/tools/personas.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
|
|
2
|
+
import { type LeadifyClient } from "../client.js";
|
|
3
|
+
export declare const CRM_SCHEMA_PACKS: readonly ["base_person", "base_company", "healthcare", "medtech", "recruitment_hr", "community_creator"];
|
|
4
|
+
type PersonaClient = Pick<LeadifyClient, "get" | "post" | "delete">;
|
|
5
|
+
export declare function crmSchemaPackSelectionError(packs: readonly string[], workflowMode?: "person_first" | "company_first" | null): string | null;
|
|
6
|
+
export declare function registerPersonaTools(server: McpServer, client?: PersonaClient): void;
|
|
7
|
+
export {};
|
package/dist/tools/personas.js
CHANGED
|
@@ -55,6 +55,41 @@ const icpStrategyShape = {
|
|
|
55
55
|
keyObjections: z.array(z.string()).optional(),
|
|
56
56
|
decisionDrivers: z.array(z.string()).optional(),
|
|
57
57
|
};
|
|
58
|
+
export const CRM_SCHEMA_PACKS = [
|
|
59
|
+
"base_person",
|
|
60
|
+
"base_company",
|
|
61
|
+
"healthcare",
|
|
62
|
+
"medtech",
|
|
63
|
+
"recruitment_hr",
|
|
64
|
+
"community_creator",
|
|
65
|
+
];
|
|
66
|
+
const SECTOR_BASE_COMPATIBILITY = {
|
|
67
|
+
healthcare: ["base_person", "base_company"],
|
|
68
|
+
medtech: ["base_person", "base_company"],
|
|
69
|
+
recruitment_hr: ["base_person", "base_company"],
|
|
70
|
+
community_creator: ["base_person", "base_company"],
|
|
71
|
+
};
|
|
72
|
+
export function crmSchemaPackSelectionError(packs, workflowMode) {
|
|
73
|
+
const unknown = [...new Set(packs.filter(pack => !CRM_SCHEMA_PACKS.includes(pack)))];
|
|
74
|
+
if (unknown.length > 0)
|
|
75
|
+
return `Unknown CRM Schema Pack: ${unknown.sort().join(", ")}. Call list_crm_schema_packs first.`;
|
|
76
|
+
const selected = [...new Set(packs)];
|
|
77
|
+
const bases = selected.filter(pack => pack === "base_person" || pack === "base_company");
|
|
78
|
+
const sectors = selected.filter(pack => pack !== "base_person" && pack !== "base_company");
|
|
79
|
+
if (bases.length !== 1 || sectors.length > 1 || selected.length !== packs.length) {
|
|
80
|
+
return "Select exactly one base CRM Schema Pack and at most one sector pack; duplicates are not allowed.";
|
|
81
|
+
}
|
|
82
|
+
const base = bases[0];
|
|
83
|
+
if (workflowMode !== undefined && workflowMode !== null) {
|
|
84
|
+
const expectedBase = workflowMode === "company_first" ? "base_company" : "base_person";
|
|
85
|
+
if (base !== expectedBase)
|
|
86
|
+
return `${base} is incompatible with workflow_mode=${workflowMode}; use ${expectedBase}.`;
|
|
87
|
+
}
|
|
88
|
+
const incompatibleSector = sectors.find(sector => !SECTOR_BASE_COMPATIBILITY[sector].includes(base));
|
|
89
|
+
return incompatibleSector
|
|
90
|
+
? `${incompatibleSector} is incompatible with ${base}. Call list_crm_schema_packs for compatible combinations.`
|
|
91
|
+
: null;
|
|
92
|
+
}
|
|
58
93
|
// camelCase mapping for nested objects that the backend expects as-is.
|
|
59
94
|
function mapLookalikeClient(c) {
|
|
60
95
|
const out = { name: c.name };
|
|
@@ -139,10 +174,10 @@ class PersonaPatchError extends Error {
|
|
|
139
174
|
* Strategy: fast-path the direct GET; on 404, fall back to the permissive
|
|
140
175
|
* list endpoint and find by id. Other errors propagate immediately.
|
|
141
176
|
*/
|
|
142
|
-
async function fetchPersona(id) {
|
|
177
|
+
async function fetchPersona(id, client = getClient()) {
|
|
143
178
|
const directPath = `/api/persona/${encodeURIComponent(id)}`;
|
|
144
179
|
try {
|
|
145
|
-
const data = await
|
|
180
|
+
const data = await client.get(directPath);
|
|
146
181
|
if (!data || typeof data !== "object") {
|
|
147
182
|
throw new PersonaPatchError("read", directPath, null, data, "Unexpected response shape (expected an object).");
|
|
148
183
|
}
|
|
@@ -162,7 +197,7 @@ async function fetchPersona(id) {
|
|
|
162
197
|
const listPath = "/api/persona";
|
|
163
198
|
let listed;
|
|
164
199
|
try {
|
|
165
|
-
listed = await
|
|
200
|
+
listed = await client.get(listPath);
|
|
166
201
|
}
|
|
167
202
|
catch (err) {
|
|
168
203
|
if (err instanceof LeadifyApiError) {
|
|
@@ -183,10 +218,10 @@ async function fetchPersona(id) {
|
|
|
183
218
|
}
|
|
184
219
|
return found;
|
|
185
220
|
}
|
|
186
|
-
async function postPersonaUpdate(id, name, fieldsToWrite) {
|
|
221
|
+
async function postPersonaUpdate(id, name, fieldsToWrite, client = getClient()) {
|
|
187
222
|
const path = "/api/persona";
|
|
188
223
|
try {
|
|
189
|
-
return await
|
|
224
|
+
return await client.post(path, {
|
|
190
225
|
id,
|
|
191
226
|
name,
|
|
192
227
|
...fieldsToWrite,
|
|
@@ -227,7 +262,7 @@ function handlePersonaToolError(error) {
|
|
|
227
262
|
return handleToolError(error);
|
|
228
263
|
}
|
|
229
264
|
// ─── Tool registrations ────────────────────────────────────────────────────
|
|
230
|
-
export function registerPersonaTools(server) {
|
|
265
|
+
export function registerPersonaTools(server, client) {
|
|
231
266
|
// ════════════════════════════════════════════════════════════════════════
|
|
232
267
|
// ESCAPE HATCH — wholesale upsert
|
|
233
268
|
// ════════════════════════════════════════════════════════════════════════
|
|
@@ -297,6 +332,12 @@ export function registerPersonaTools(server) {
|
|
|
297
332
|
.nullable()
|
|
298
333
|
.optional()
|
|
299
334
|
.describe("Qualification mode."),
|
|
335
|
+
schema_packs: z
|
|
336
|
+
.array(z.enum(CRM_SCHEMA_PACKS))
|
|
337
|
+
.optional()
|
|
338
|
+
.describe("Canonical CRM Schema Packs. REQUIRED on create: exactly one base pack " +
|
|
339
|
+
"(base_person for person_first or base_company for company_first) and at most one " +
|
|
340
|
+
"compatible sector pack. Call list_crm_schema_packs before selecting values."),
|
|
300
341
|
output_language: z
|
|
301
342
|
.string()
|
|
302
343
|
.nullable()
|
|
@@ -345,6 +386,14 @@ export function registerPersonaTools(server) {
|
|
|
345
386
|
.describe("Snippets inserted at named points. scope: 'qualify' | 'outreach'; insertion_point: 'prefix' | 'phase_1_5' | 'phase_2_extra' | 'suffix'."),
|
|
346
387
|
}, async (params) => {
|
|
347
388
|
try {
|
|
389
|
+
if (params.id === undefined && params.schema_packs === undefined) {
|
|
390
|
+
throw new Error("schema_packs is required when creating a Persona. Call list_crm_schema_packs, then select exactly one base pack and at most one compatible sector pack.");
|
|
391
|
+
}
|
|
392
|
+
if (params.schema_packs !== undefined) {
|
|
393
|
+
const selectionError = crmSchemaPackSelectionError(params.schema_packs, params.workflow_mode ?? (params.id === undefined ? "person_first" : undefined));
|
|
394
|
+
if (selectionError)
|
|
395
|
+
throw new Error(selectionError);
|
|
396
|
+
}
|
|
348
397
|
const body = { name: params.name };
|
|
349
398
|
if (params.id !== undefined)
|
|
350
399
|
body.id = params.id;
|
|
@@ -376,6 +425,8 @@ export function registerPersonaTools(server) {
|
|
|
376
425
|
body.strictMatch = params.strict_match;
|
|
377
426
|
if (params.workflow_mode !== undefined)
|
|
378
427
|
body.workflowMode = params.workflow_mode;
|
|
428
|
+
if (params.schema_packs !== undefined)
|
|
429
|
+
body.schemaPacks = params.schema_packs;
|
|
379
430
|
if (params.output_language !== undefined)
|
|
380
431
|
body.outputLanguage = params.output_language;
|
|
381
432
|
if (params.enable_signals !== undefined)
|
|
@@ -404,7 +455,32 @@ export function registerPersonaTools(server) {
|
|
|
404
455
|
if (params.extra_prompt_phases !== undefined)
|
|
405
456
|
body.extraPromptPhases =
|
|
406
457
|
params.extra_prompt_phases.map(mapExtraPromptPhase);
|
|
407
|
-
const data = await getClient().post("/api/persona", body);
|
|
458
|
+
const data = await (client ?? getClient()).post("/api/persona", body);
|
|
459
|
+
return toolResult(data);
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
return handlePersonaToolError(error);
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
// ── update_persona_schema_packs ───────────────────────────────────────
|
|
466
|
+
server.tool("update_persona_schema_packs", "Safely replace one Persona's CRM Schema Packs without rewriting unrelated Persona sections. " +
|
|
467
|
+
"Reads the current Persona, validates exactly one workflow-compatible base and at most one " +
|
|
468
|
+
"compatible sector pack, then writes schemaPacks through the canonical Persona endpoint. " +
|
|
469
|
+
"The response includes the persisted Persona and readiness proofs for every linked group.", {
|
|
470
|
+
id: z.string().describe("Persona ID to update."),
|
|
471
|
+
schema_packs: z
|
|
472
|
+
.array(z.enum(CRM_SCHEMA_PACKS))
|
|
473
|
+
.min(1)
|
|
474
|
+
.describe("Complete replacement selection. Call list_crm_schema_packs first."),
|
|
475
|
+
}, async ({ id, schema_packs }) => {
|
|
476
|
+
try {
|
|
477
|
+
const api = client ?? getClient();
|
|
478
|
+
const persona = await fetchPersona(id, api);
|
|
479
|
+
const workflowMode = persona.workflowMode === "company_first" ? "company_first" : "person_first";
|
|
480
|
+
const selectionError = crmSchemaPackSelectionError(schema_packs, workflowMode);
|
|
481
|
+
if (selectionError)
|
|
482
|
+
throw new Error(selectionError);
|
|
483
|
+
const data = await postPersonaUpdate(id, persona.name, { schemaPacks: schema_packs }, api);
|
|
408
484
|
return toolResult(data);
|
|
409
485
|
}
|
|
410
486
|
catch (error) {
|
|
@@ -428,7 +504,7 @@ export function registerPersonaTools(server) {
|
|
|
428
504
|
"in its own tool)."),
|
|
429
505
|
}, async ({ id, include_outreach }) => {
|
|
430
506
|
try {
|
|
431
|
-
const data = await getClient().get(`/api/persona/${encodeURIComponent(id)}`);
|
|
507
|
+
const data = await (client ?? getClient()).get(`/api/persona/${encodeURIComponent(id)}`);
|
|
432
508
|
// SILO defense in depth: strip the outreach trio locally unless the
|
|
433
509
|
// caller explicitly opted in. Outreach data belongs to the dedicated
|
|
434
510
|
// outreach tools — never mix it with the persona read by default.
|
|
@@ -470,14 +546,14 @@ export function registerPersonaTools(server) {
|
|
|
470
546
|
// Step 1: resolve the persona assigned to this lead group (slim view).
|
|
471
547
|
// We only need the persona's id from here — we re-fetch the full
|
|
472
548
|
// persona below to grab the outreach trio.
|
|
473
|
-
const groupPersona = (await getClient().get(`/api/lead-group/${encodeURIComponent(lead_group_id)}/persona`));
|
|
549
|
+
const groupPersona = (await (client ?? getClient()).get(`/api/lead-group/${encodeURIComponent(lead_group_id)}/persona`));
|
|
474
550
|
const personaId = groupPersona?.persona?.id;
|
|
475
551
|
if (!personaId) {
|
|
476
552
|
return toolError(`No persona is assigned to lead group "${lead_group_id}". ` +
|
|
477
553
|
`Assign one via update_lead_group({ persona_id }) before reading outreach templates.`);
|
|
478
554
|
}
|
|
479
555
|
// Step 2: fetch the full persona so we can project the trio.
|
|
480
|
-
const full = (await getClient().get(`/api/persona/${encodeURIComponent(personaId)}`));
|
|
556
|
+
const full = (await (client ?? getClient()).get(`/api/persona/${encodeURIComponent(personaId)}`));
|
|
481
557
|
// The API may return either a bare persona or {persona: {...}}.
|
|
482
558
|
const persona = full.persona ?? full;
|
|
483
559
|
const trio = {
|
|
@@ -523,7 +599,7 @@ export function registerPersonaTools(server) {
|
|
|
523
599
|
params.set("organizationId", organization_id);
|
|
524
600
|
if (include_globals !== undefined)
|
|
525
601
|
params.set("includeGlobals", String(include_globals));
|
|
526
|
-
const data = await getClient().get("/api/persona", params);
|
|
602
|
+
const data = await (client ?? getClient()).get("/api/persona", params);
|
|
527
603
|
if (verbose)
|
|
528
604
|
return toolResult(data);
|
|
529
605
|
// Project to compact view: id, name, organizationId, truncated description.
|
|
@@ -562,7 +638,7 @@ export function registerPersonaTools(server) {
|
|
|
562
638
|
id: z.string().describe("Persona ID to delete."),
|
|
563
639
|
}, async ({ id }) => {
|
|
564
640
|
try {
|
|
565
|
-
const data = await getClient().delete(`/api/persona/${encodeURIComponent(id)}`);
|
|
641
|
+
const data = await (client ?? getClient()).delete(`/api/persona/${encodeURIComponent(id)}`);
|
|
566
642
|
return toolResult(data);
|
|
567
643
|
}
|
|
568
644
|
catch (error) {
|
|
@@ -580,7 +656,7 @@ export function registerPersonaTools(server) {
|
|
|
580
656
|
.describe("Target organization ID (Clerk org ID). Pass null to make the persona global."),
|
|
581
657
|
}, async ({ id, organization_id }) => {
|
|
582
658
|
try {
|
|
583
|
-
const data = await getClient().post(`/api/persona/${encodeURIComponent(id)}/move`, { organizationId: organization_id });
|
|
659
|
+
const data = await (client ?? getClient()).post(`/api/persona/${encodeURIComponent(id)}/move`, { organizationId: organization_id });
|
|
584
660
|
return toolResult(data);
|
|
585
661
|
}
|
|
586
662
|
catch (error) {
|
|
@@ -625,7 +701,7 @@ export function registerPersonaTools(server) {
|
|
|
625
701
|
enable_signals === undefined) {
|
|
626
702
|
return toolError("Provide at least one field to update.");
|
|
627
703
|
}
|
|
628
|
-
const current = await fetchPersona(id);
|
|
704
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
629
705
|
const body = {};
|
|
630
706
|
if (description !== undefined)
|
|
631
707
|
body.description = description;
|
|
@@ -637,7 +713,7 @@ export function registerPersonaTools(server) {
|
|
|
637
713
|
body.strictMatch = strict_match;
|
|
638
714
|
if (enable_signals !== undefined)
|
|
639
715
|
body.enableSignals = enable_signals;
|
|
640
|
-
const data = await postPersonaUpdate(id, name ?? current.name, body);
|
|
716
|
+
const data = await postPersonaUpdate(id, name ?? current.name, body, client ?? getClient());
|
|
641
717
|
return toolResult(data);
|
|
642
718
|
}
|
|
643
719
|
catch (error) {
|
|
@@ -665,11 +741,11 @@ export function registerPersonaTools(server) {
|
|
|
665
741
|
(remove_keys === undefined || remove_keys.length === 0)) {
|
|
666
742
|
return toolError("Provide at least one of: patch (non-empty), remove_keys (non-empty).");
|
|
667
743
|
}
|
|
668
|
-
const current = await fetchPersona(id);
|
|
744
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
669
745
|
const next = { ...asObject(current.targetProfile), ...(patch ?? {}) };
|
|
670
746
|
for (const k of remove_keys ?? [])
|
|
671
747
|
delete next[k];
|
|
672
|
-
const data = await postPersonaUpdate(id, current.name, { targetProfile: next });
|
|
748
|
+
const data = await postPersonaUpdate(id, current.name, { targetProfile: next }, client ?? getClient());
|
|
673
749
|
return toolResult(data);
|
|
674
750
|
}
|
|
675
751
|
catch (error) {
|
|
@@ -694,11 +770,11 @@ export function registerPersonaTools(server) {
|
|
|
694
770
|
(remove_keys === undefined || remove_keys.length === 0)) {
|
|
695
771
|
return toolError("Provide at least one of: patch (non-empty), remove_keys (non-empty).");
|
|
696
772
|
}
|
|
697
|
-
const current = await fetchPersona(id);
|
|
773
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
698
774
|
const next = { ...asObject(current.disqualificationCriteria), ...(patch ?? {}) };
|
|
699
775
|
for (const k of remove_keys ?? [])
|
|
700
776
|
delete next[k];
|
|
701
|
-
const data = await postPersonaUpdate(id, current.name, { disqualificationCriteria: next });
|
|
777
|
+
const data = await postPersonaUpdate(id, current.name, { disqualificationCriteria: next }, client ?? getClient());
|
|
702
778
|
return toolResult(data);
|
|
703
779
|
}
|
|
704
780
|
catch (error) {
|
|
@@ -730,7 +806,7 @@ export function registerPersonaTools(server) {
|
|
|
730
806
|
if (tools !== undefined && (add !== undefined || remove !== undefined)) {
|
|
731
807
|
return toolError("'tools' is mutually exclusive with 'add' / 'remove'.");
|
|
732
808
|
}
|
|
733
|
-
const current = await fetchPersona(id);
|
|
809
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
734
810
|
let next;
|
|
735
811
|
if (tools !== undefined) {
|
|
736
812
|
next = tools;
|
|
@@ -743,7 +819,7 @@ export function registerPersonaTools(server) {
|
|
|
743
819
|
set.delete(t);
|
|
744
820
|
next = Array.from(set);
|
|
745
821
|
}
|
|
746
|
-
const data = await postPersonaUpdate(id, current.name, { activeTools: next });
|
|
822
|
+
const data = await postPersonaUpdate(id, current.name, { activeTools: next }, client ?? getClient());
|
|
747
823
|
return toolResult(data);
|
|
748
824
|
}
|
|
749
825
|
catch (error) {
|
|
@@ -781,7 +857,7 @@ export function registerPersonaTools(server) {
|
|
|
781
857
|
disqualified: "disqualifiedCriteria",
|
|
782
858
|
};
|
|
783
859
|
const field = fieldMap[tier];
|
|
784
|
-
const current = await fetchPersona(id);
|
|
860
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
785
861
|
let nextValue;
|
|
786
862
|
if (typeof criteria === "string") {
|
|
787
863
|
nextValue = criteria;
|
|
@@ -793,7 +869,7 @@ export function registerPersonaTools(server) {
|
|
|
793
869
|
: {};
|
|
794
870
|
nextValue = { ...base, ...criteria };
|
|
795
871
|
}
|
|
796
|
-
const data = await postPersonaUpdate(id, current.name, { [field]: nextValue });
|
|
872
|
+
const data = await postPersonaUpdate(id, current.name, { [field]: nextValue }, client ?? getClient());
|
|
797
873
|
return toolResult(data);
|
|
798
874
|
}
|
|
799
875
|
catch (error) {
|
|
@@ -816,29 +892,29 @@ export function registerPersonaTools(server) {
|
|
|
816
892
|
client: lookalikeClient
|
|
817
893
|
.optional()
|
|
818
894
|
.describe("Client payload. Required for 'add' and 'replace'. Schema: {name, sector?, size?, segment?, comparison_criteria?}."),
|
|
819
|
-
}, async ({ id, action, name, client }) => {
|
|
895
|
+
}, async ({ id, action, name, client: lookalikeClientInput }) => {
|
|
820
896
|
try {
|
|
821
|
-
if ((action === "add" || action === "replace") && !
|
|
897
|
+
if ((action === "add" || action === "replace") && !lookalikeClientInput) {
|
|
822
898
|
return toolError(`client is required for action '${action}'.`);
|
|
823
899
|
}
|
|
824
900
|
if ((action === "replace" || action === "remove") && !name) {
|
|
825
901
|
return toolError(`name is required for action '${action}'.`);
|
|
826
902
|
}
|
|
827
|
-
const current = await fetchPersona(id);
|
|
903
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
828
904
|
const list = asArray(current.lookalikeClients).map((c) => ({ ...c }));
|
|
829
905
|
if (action === "add") {
|
|
830
|
-
const newName =
|
|
906
|
+
const newName = lookalikeClientInput.name;
|
|
831
907
|
if (list.some((c) => c.name === newName)) {
|
|
832
908
|
return toolError(`A lookalike client with name "${newName}" already exists. Use action 'replace' instead.`);
|
|
833
909
|
}
|
|
834
|
-
list.push(mapLookalikeClient(
|
|
910
|
+
list.push(mapLookalikeClient(lookalikeClientInput));
|
|
835
911
|
}
|
|
836
912
|
else if (action === "replace") {
|
|
837
913
|
const idx = list.findIndex((c) => c.name === name);
|
|
838
914
|
if (idx === -1) {
|
|
839
915
|
return toolError(`No lookalike client found with name "${name}".`);
|
|
840
916
|
}
|
|
841
|
-
list[idx] = mapLookalikeClient(
|
|
917
|
+
list[idx] = mapLookalikeClient(lookalikeClientInput);
|
|
842
918
|
}
|
|
843
919
|
else {
|
|
844
920
|
const before = list.length;
|
|
@@ -851,7 +927,7 @@ export function registerPersonaTools(server) {
|
|
|
851
927
|
}
|
|
852
928
|
const data = await postPersonaUpdate(id, current.name, {
|
|
853
929
|
lookalikeClients: list,
|
|
854
|
-
});
|
|
930
|
+
}, client ?? getClient());
|
|
855
931
|
return toolResult(data);
|
|
856
932
|
}
|
|
857
933
|
catch (error) {
|
|
@@ -883,7 +959,7 @@ export function registerPersonaTools(server) {
|
|
|
883
959
|
if ((action === "replace" || action === "remove") && index === undefined) {
|
|
884
960
|
return toolError(`index is required for action '${action}'.`);
|
|
885
961
|
}
|
|
886
|
-
const current = await fetchPersona(id);
|
|
962
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
887
963
|
const list = asArray(current.painPoints).map((p) => ({ ...p }));
|
|
888
964
|
if (action === "add") {
|
|
889
965
|
list.push(pain);
|
|
@@ -900,7 +976,7 @@ export function registerPersonaTools(server) {
|
|
|
900
976
|
}
|
|
901
977
|
list.splice(index, 1);
|
|
902
978
|
}
|
|
903
|
-
const data = await postPersonaUpdate(id, current.name, { painPoints: list });
|
|
979
|
+
const data = await postPersonaUpdate(id, current.name, { painPoints: list }, client ?? getClient());
|
|
904
980
|
return toolResult(data);
|
|
905
981
|
}
|
|
906
982
|
catch (error) {
|
|
@@ -941,7 +1017,7 @@ export function registerPersonaTools(server) {
|
|
|
941
1017
|
context === undefined) {
|
|
942
1018
|
return toolError("Provide at least one field to update.");
|
|
943
1019
|
}
|
|
944
|
-
const current = await fetchPersona(id);
|
|
1020
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
945
1021
|
const body = {};
|
|
946
1022
|
if (normalization_rules !== undefined)
|
|
947
1023
|
body.normalizationRules = normalization_rules;
|
|
@@ -951,7 +1027,7 @@ export function registerPersonaTools(server) {
|
|
|
951
1027
|
body.toneInstructions = tone_instructions;
|
|
952
1028
|
if (context !== undefined)
|
|
953
1029
|
body.context = context;
|
|
954
|
-
const data = await postPersonaUpdate(id, current.name, body);
|
|
1030
|
+
const data = await postPersonaUpdate(id, current.name, body, client ?? getClient());
|
|
955
1031
|
return toolResult(data);
|
|
956
1032
|
}
|
|
957
1033
|
catch (error) {
|
|
@@ -1004,7 +1080,7 @@ export function registerPersonaTools(server) {
|
|
|
1004
1080
|
(remove_keys === undefined || remove_keys.length === 0)) {
|
|
1005
1081
|
return toolError("Provide at least one field to update.");
|
|
1006
1082
|
}
|
|
1007
|
-
const current = await fetchPersona(id);
|
|
1083
|
+
const current = await fetchPersona(id, client ?? getClient());
|
|
1008
1084
|
const next = { ...asObject(current.icpStrategy) };
|
|
1009
1085
|
if (dealType !== undefined)
|
|
1010
1086
|
next.dealType = dealType;
|
|
@@ -1020,7 +1096,7 @@ export function registerPersonaTools(server) {
|
|
|
1020
1096
|
Object.assign(next, patch);
|
|
1021
1097
|
for (const k of remove_keys ?? [])
|
|
1022
1098
|
delete next[k];
|
|
1023
|
-
const data = await postPersonaUpdate(id, current.name, { icpStrategy: next });
|
|
1099
|
+
const data = await postPersonaUpdate(id, current.name, { icpStrategy: next }, client ?? getClient());
|
|
1024
1100
|
return toolResult(data);
|
|
1025
1101
|
}
|
|
1026
1102
|
catch (error) {
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getClient } from "../client.js";
|
|
3
|
+
import { handleToolError, toolResult } from "../types.js";
|
|
4
|
+
const entityKind = z.enum(["ACCOUNT", "LEGAL_ENTITY", "ESTABLISHMENT", "PERSON", "EMPLOYMENT"]);
|
|
5
|
+
const relationshipType = z.enum(["EMPLOYED_BY", "AFFILIATED_WITH", "PART_OF", "COLLEAGUE_OF"]);
|
|
6
|
+
const entityRef = z.object({ kind: entityKind, id: z.string().min(1) }).strict();
|
|
7
|
+
const confidence = z.object({
|
|
8
|
+
value: z.number().min(0).max(1),
|
|
9
|
+
method: z.string().min(1).max(100),
|
|
10
|
+
assessed_at: z.string().datetime(),
|
|
11
|
+
}).strict();
|
|
12
|
+
export function registerRelationshipTools(server, client = getClient()) {
|
|
13
|
+
server.tool("list_canonical_relationships", "List tenant-scoped canonical relationships. The response is direction-independent for consumers: filter by one entity to find both incoming and outgoing links. Legacy lead relation fields are compatibility projections, not the source of truth.", {
|
|
14
|
+
organization_id: z.string().min(1),
|
|
15
|
+
entity_kind: entityKind.optional(),
|
|
16
|
+
entity_id: z.string().min(1).optional(),
|
|
17
|
+
relationship_type: relationshipType.optional(),
|
|
18
|
+
state: z.enum(["ACTIVE", "TOMBSTONED"]).optional().default("ACTIVE"),
|
|
19
|
+
limit: z.number().int().min(1).max(500).optional().default(100),
|
|
20
|
+
cursor: z.string().optional(),
|
|
21
|
+
}, async ({ organization_id, entity_kind, entity_id, relationship_type, state, limit, cursor }) => {
|
|
22
|
+
try {
|
|
23
|
+
if (Boolean(entity_kind) !== Boolean(entity_id))
|
|
24
|
+
return toolResult({ error: "entity_kind and entity_id must be provided together" });
|
|
25
|
+
const params = new URLSearchParams({ organizationId: organization_id, state, limit: String(limit) });
|
|
26
|
+
if (entity_kind)
|
|
27
|
+
params.set("entityKind", entity_kind);
|
|
28
|
+
if (entity_id)
|
|
29
|
+
params.set("entityId", entity_id);
|
|
30
|
+
if (relationship_type)
|
|
31
|
+
params.set("relationshipType", relationship_type);
|
|
32
|
+
if (cursor)
|
|
33
|
+
params.set("cursor", cursor);
|
|
34
|
+
return toolResult(await client.get("/api/canonical-relationships", params));
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return handleToolError(error);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
server.tool("create_canonical_relationship", "Create one typed canonical relationship in a tenant. Endpoints must already be strong canonical identities in that tenant. Retries with the same idempotency key and payload are safe; a changed payload is rejected. This tool never creates placeholder identities or activates outreach.", {
|
|
41
|
+
organization_id: z.string().min(1), relationship_type: relationshipType,
|
|
42
|
+
source: entityRef, target: entityRef,
|
|
43
|
+
trust_state: z.enum(["CONFIRMED", "PROPOSED", "AMBIGUOUS", "CONFLICTED", "UNKNOWN"]).optional().default("UNKNOWN"),
|
|
44
|
+
confidence: confidence.optional(), evidence_ids: z.array(z.string()).max(100).optional().default([]),
|
|
45
|
+
valid_from: z.string().datetime().nullable().optional(), valid_to: z.string().datetime().nullable().optional(),
|
|
46
|
+
attributes: z.record(z.unknown()).optional(), idempotency_key: z.string().min(1).max(255),
|
|
47
|
+
}, async (params) => {
|
|
48
|
+
try {
|
|
49
|
+
return toolResult(await client.post("/api/canonical-relationships", {
|
|
50
|
+
organizationId: params.organization_id, relationshipType: params.relationship_type,
|
|
51
|
+
source: params.source, target: params.target, trustState: params.trust_state,
|
|
52
|
+
confidence: params.confidence ? { ...params.confidence, assessedAt: params.confidence.assessed_at, assessed_at: undefined } : undefined,
|
|
53
|
+
evidenceIds: params.evidence_ids, validFrom: params.valid_from, validTo: params.valid_to,
|
|
54
|
+
attributes: params.attributes, idempotencyKey: params.idempotency_key,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return handleToolError(error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
server.tool("update_canonical_relationship", "Update, tombstone, or restore one canonical relationship through the shared transactional service. Every mutation is versioned in the immutable ledger and requires a unique idempotency key.", {
|
|
62
|
+
organization_id: z.string().min(1), relationship_id: z.string().min(1),
|
|
63
|
+
relationship_type: relationshipType.optional(), source: entityRef.optional(), target: entityRef.optional(),
|
|
64
|
+
trust_state: z.enum(["CONFIRMED", "PROPOSED", "AMBIGUOUS", "CONFLICTED", "UNKNOWN"]).optional(),
|
|
65
|
+
state: z.enum(["ACTIVE", "TOMBSTONED"]).optional(), evidence_ids: z.array(z.string()).max(100).optional(),
|
|
66
|
+
valid_from: z.string().datetime().nullable().optional(), valid_to: z.string().datetime().nullable().optional(),
|
|
67
|
+
attributes: z.record(z.unknown()).nullable().optional(), idempotency_key: z.string().min(1).max(255),
|
|
68
|
+
}, async (params) => {
|
|
69
|
+
try {
|
|
70
|
+
return toolResult(await client.patch(`/api/canonical-relationships/${encodeURIComponent(params.relationship_id)}`, {
|
|
71
|
+
organizationId: params.organization_id, relationshipType: params.relationship_type,
|
|
72
|
+
source: params.source, target: params.target, trustState: params.trust_state, state: params.state,
|
|
73
|
+
evidenceIds: params.evidence_ids, validFrom: params.valid_from, validTo: params.valid_to,
|
|
74
|
+
attributes: params.attributes, idempotencyKey: params.idempotency_key,
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return handleToolError(error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
server.tool("tombstone_canonical_relationship", "Soft-delete one canonical relationship while preserving its evidence and immutable history. Use this instead of deleting legacy relation fields.", { organization_id: z.string().min(1), relationship_id: z.string().min(1), idempotency_key: z.string().min(1).max(255) }, async ({ organization_id, relationship_id, idempotency_key }) => {
|
|
82
|
+
try {
|
|
83
|
+
return toolResult(await client.delete(`/api/canonical-relationships/${encodeURIComponent(relationship_id)}`, { organizationId: organization_id, idempotencyKey: idempotency_key }));
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return handleToolError(error);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const migrationFields = {
|
|
90
|
+
organization_id: z.string().min(1), source_group_id: z.string().min(1), field: z.string().min(1),
|
|
91
|
+
relationship_type: relationshipType, endpoint_role: z.enum(["SOURCE", "TARGET"]),
|
|
92
|
+
source_label: z.string().min(1).max(120), target_label: z.string().min(1).max(120),
|
|
93
|
+
limit: z.number().int().min(1).max(500).optional().default(500),
|
|
94
|
+
};
|
|
95
|
+
const migrationBody = (params) => ({
|
|
96
|
+
organizationId: params.organization_id, sourceGroupId: params.source_group_id, field: params.field,
|
|
97
|
+
relationshipType: params.relationship_type, endpointRole: params.endpoint_role,
|
|
98
|
+
sourceLabel: params.source_label, targetLabel: params.target_label, limit: params.limit,
|
|
99
|
+
});
|
|
100
|
+
server.tool("preview_canonical_relationship_migration", "Read-only planner for a legacy LeadGroup relation. Returns the exact forward/inverse divergence matrix, unresolved identities, quarantine candidates, and a digest. It creates no identities, links, placeholders, campaigns, or outreach.", migrationFields, async (params) => {
|
|
101
|
+
try {
|
|
102
|
+
return toolResult(await client.post("/api/canonical-relationships/migration/preview", migrationBody(params)));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return handleToolError(error);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
server.tool("apply_canonical_relationship_migration", "Apply exactly a previously reviewed canonical relationship migration plan. Requires its digest; the backend rejects drift, limits the batch, quarantines unresolved identities, and keeps external activation at zero.", { ...migrationFields, expected_plan_digest: z.string().regex(/^[a-f0-9]{64}$/) }, async (params) => {
|
|
109
|
+
try {
|
|
110
|
+
return toolResult(await client.post("/api/canonical-relationships/migration/apply", { ...migrationBody(params), expectedPlanDigest: params.expected_plan_digest }));
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
return handleToolError(error);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
server.tool("rollback_canonical_relationship_migration", "Tombstone every active relationship created by one migration digest. This is reversible and does not delete identities, evidence, leads, groups, or history.", { organization_id: z.string().min(1), plan_digest: z.string().regex(/^[a-f0-9]{64}$/), idempotency_key: z.string().min(1).max(200) }, async ({ organization_id, plan_digest, idempotency_key }) => {
|
|
117
|
+
try {
|
|
118
|
+
return toolResult(await client.post("/api/canonical-relationships/migration/rollback", { organizationId: organization_id, planDigest: plan_digest, idempotencyKey: idempotency_key }));
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return handleToolError(error);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
package/dist/tools/schema.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
|
|
2
|
+
import { type LeadifyClient } from "../client.js";
|
|
3
|
+
type SchemaClient = Pick<LeadifyClient, "get" | "post">;
|
|
4
|
+
export declare function registerSchemaTools(server: McpServer, client?: SchemaClient): void;
|
|
5
|
+
export {};
|
package/dist/tools/schema.js
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { getClient } from "../client.js";
|
|
3
3
|
import { toolResult, handleToolError } from "../types.js";
|
|
4
|
-
export function registerSchemaTools(server) {
|
|
4
|
+
export function registerSchemaTools(server, client) {
|
|
5
|
+
// ── list_crm_schema_packs ──────────────────────────────────────────────
|
|
6
|
+
server.tool("list_crm_schema_packs", "List the canonical CRM Schema Pack catalogue before creating or updating a Persona. " +
|
|
7
|
+
"Returns every pack id, label, domain, base/sector kind, compatible base packs and " +
|
|
8
|
+
"workflow modes, field count, registry version and composition constraints. The catalogue " +
|
|
9
|
+
"is global code configuration, not organization-owned data.", {}, async () => {
|
|
10
|
+
try {
|
|
11
|
+
const data = await (client ?? getClient()).get("/api/persona-types");
|
|
12
|
+
return toolResult(data);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
return handleToolError(error);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
5
18
|
// ── update_schema ──────────────────────────────────────────────────────
|
|
6
19
|
server.tool("update_schema", "Add or modify field definitions in a lead group's schema. Each field update " +
|
|
7
20
|
"specifies the internal field name, display label, data type, whether it's required, " +
|
|
@@ -58,7 +71,7 @@ export function registerSchemaTools(server) {
|
|
|
58
71
|
...(u.options ? { options: u.options } : {}),
|
|
59
72
|
})),
|
|
60
73
|
};
|
|
61
|
-
const data = await getClient().post("/update-schema", body);
|
|
74
|
+
const data = await (client ?? getClient()).post("/update-schema", body);
|
|
62
75
|
return toolResult(data);
|
|
63
76
|
}
|
|
64
77
|
catch (error) {
|
|
@@ -88,7 +101,7 @@ export function registerSchemaTools(server) {
|
|
|
88
101
|
else {
|
|
89
102
|
body.columnNames = column_names;
|
|
90
103
|
}
|
|
91
|
-
const data = await getClient().post("/delete-column", body);
|
|
104
|
+
const data = await (client ?? getClient()).post("/delete-column", body);
|
|
92
105
|
return toolResult(data);
|
|
93
106
|
}
|
|
94
107
|
catch (error) {
|
|
@@ -120,7 +133,7 @@ export function registerSchemaTools(server) {
|
|
|
120
133
|
body.addHiddenColumns = add_hidden;
|
|
121
134
|
if (remove_hidden)
|
|
122
135
|
body.removeHiddenColumns = remove_hidden;
|
|
123
|
-
const data = await getClient().post("/update-hidden-columns", body);
|
|
136
|
+
const data = await (client ?? getClient()).post("/update-hidden-columns", body);
|
|
124
137
|
return toolResult(data);
|
|
125
138
|
}
|
|
126
139
|
catch (error) {
|