@agifyai/leadify-mcp 8.6.4 → 8.6.6
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 +3 -4
- package/dist/client.d.ts +6 -0
- package/dist/client.js +13 -0
- package/dist/tools/lead_groups.js +0 -42
- package/dist/tools/organizations.js +2 -2
- package/dist/tools/schema.d.ts +1 -1
- package/dist/tools/schema.js +36 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -291,11 +291,10 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
291
291
|
| `rollback_canonical_relationship_migration` | Tombstoner les liens créés par un plan de migration précis. |
|
|
292
292
|
| `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
293
|
| `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
|
-
| `
|
|
294
|
+
| `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. |
|
|
295
297
|
| `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. |
|
|
296
|
-
| `update_persona_schema_packs` | Remplacer uniquement les CRM Schema Packs d’un Persona tenant-scoped avec `expected_updated_at`, puis relire la readiness des groupes liés. |
|
|
297
|
-
| `get_persona` | Récupérer un Persona par son ID et son organisation explicite. Les anciennes Personas globales/orphelines sont exclues. |
|
|
298
|
-
| `get_lead_group_persona` | Récupérer le persona assigné à un groupe de leads. |
|
|
299
298
|
| `list_data_sources` | Lister toutes les sources de données configurées (par pays puis nom). |
|
|
300
299
|
| `create_data_source` | Créer une nouvelle source de données (admin uniquement). |
|
|
301
300
|
| `update_data_source` | Modifier une source de données existante (admin uniquement). |
|
package/dist/client.d.ts
CHANGED
|
@@ -8,5 +8,11 @@ export declare class LeadifyClient {
|
|
|
8
8
|
put(path: string, body: unknown): Promise<unknown>;
|
|
9
9
|
patch(path: string, body: unknown): Promise<unknown>;
|
|
10
10
|
delete(path: string, body?: unknown): Promise<unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* Invoke an authenticated oRPC procedure. The API-key facade exposes most
|
|
13
|
+
* everyday operations as REST, while hash-pinned schema migrations remain
|
|
14
|
+
* intentionally available only through the admin oRPC boundary.
|
|
15
|
+
*/
|
|
16
|
+
rpc(path: string, input: unknown): Promise<unknown>;
|
|
11
17
|
}
|
|
12
18
|
export declare function getClient(): LeadifyClient;
|
package/dist/client.js
CHANGED
|
@@ -57,6 +57,19 @@ export class LeadifyClient {
|
|
|
57
57
|
const url = new URL(path, this.baseUrl);
|
|
58
58
|
return this.request("DELETE", url, body);
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Invoke an authenticated oRPC procedure. The API-key facade exposes most
|
|
62
|
+
* everyday operations as REST, while hash-pinned schema migrations remain
|
|
63
|
+
* intentionally available only through the admin oRPC boundary.
|
|
64
|
+
*/
|
|
65
|
+
async rpc(path, input) {
|
|
66
|
+
const url = new URL(`/rpc/${path.replace(/^\/+/, "")}`, this.baseUrl);
|
|
67
|
+
const data = await this.request("POST", url, { json: input });
|
|
68
|
+
if (data && typeof data === "object" && "json" in data) {
|
|
69
|
+
return data.json;
|
|
70
|
+
}
|
|
71
|
+
return data;
|
|
72
|
+
}
|
|
60
73
|
}
|
|
61
74
|
let _client = null;
|
|
62
75
|
export function getClient() {
|
|
@@ -170,46 +170,4 @@ export function registerLeadGroupTools(server, writeClient) {
|
|
|
170
170
|
return handleToolError(error);
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
|
-
// ── get_lead_group_persona ─────────────────────────────────────────────
|
|
174
|
-
server.tool("get_lead_group_persona", "Retrieve the persona assigned to a specific lead group, with group context. " +
|
|
175
|
-
"Returns null persona if none is assigned. Use this to check what targeting " +
|
|
176
|
-
"rules apply to a group before generating messages. " +
|
|
177
|
-
"By default, `persona` uses the OUTREACH projection (messaging + targeting), " +
|
|
178
|
-
"the lean supported view. Pass `view=full` only when you need the complete " +
|
|
179
|
-
"persona payload — prefer the dedicated get_outreach_templates tool for the outreach trio. " +
|
|
180
|
-
"SILO: outreach data is its own concern. It does not leak into the default persona payload.", {
|
|
181
|
-
lead_group_id: z.string().describe("Lead group ID."),
|
|
182
|
-
view: z
|
|
183
|
-
.enum(["full", "outreach"])
|
|
184
|
-
.optional()
|
|
185
|
-
.default("outreach")
|
|
186
|
-
.describe("Persona projection. " +
|
|
187
|
-
"'outreach' (default) = projection used by the outreach agent (messaging + targeting). " +
|
|
188
|
-
"'full' = the complete persona payload. " +
|
|
189
|
-
"Prefer get_persona + get_outreach_templates for a complete picture, scoped per concern."),
|
|
190
|
-
}, async ({ lead_group_id, view }) => {
|
|
191
|
-
try {
|
|
192
|
-
const params = new URLSearchParams();
|
|
193
|
-
if (view)
|
|
194
|
-
params.set("view", view);
|
|
195
|
-
const data = await getClient().get(`/api/lead-group/${encodeURIComponent(lead_group_id)}/persona`, params);
|
|
196
|
-
// SILO defense in depth: even if the API returns the full payload for
|
|
197
|
-
// `view=slim` (the view param is best-effort on the backend), strip the
|
|
198
|
-
// outreach trio locally so it can never leak through this tool.
|
|
199
|
-
// Outreach data belongs to the dedicated outreach tools.
|
|
200
|
-
if (view !== "outreach" && data && typeof data === "object") {
|
|
201
|
-
const wrapper = data;
|
|
202
|
-
const persona = wrapper.persona ?? null;
|
|
203
|
-
if (persona && typeof persona === "object") {
|
|
204
|
-
delete persona.outreachTemplates;
|
|
205
|
-
delete persona.outreachSignalRouting;
|
|
206
|
-
delete persona.outreachFields;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return toolResult(data);
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
return handleToolError(error);
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
173
|
}
|
|
@@ -48,7 +48,7 @@ export function registerOrganizationTools(server) {
|
|
|
48
48
|
// ── list_organizations ─────────────────────────────────────────────────
|
|
49
49
|
server.tool("list_organizations", "List every organization accessible to the caller, returning each with id, name, and " +
|
|
50
50
|
"optional slug. Use this as the starting point when you need an organization_id — " +
|
|
51
|
-
"e.g. before create_lead_group, clone_lead_group,
|
|
51
|
+
"e.g. before create_lead_group, clone_lead_group, create_persona_contract (org-scoped), " +
|
|
52
52
|
"or move_persona.", {}, async () => {
|
|
53
53
|
try {
|
|
54
54
|
const data = await getClient().get("/api/organizations");
|
|
@@ -129,7 +129,7 @@ export function registerOrganizationTools(server) {
|
|
|
129
129
|
leadGroups: groups.slice(0, limit).map((group) => ({ id: group.id, name: group.name, organizationId: group.organizationId, leadCount: group._count?.leads ?? 0, published: group.published ?? false })),
|
|
130
130
|
total: groups.length,
|
|
131
131
|
truncated: groups.length > limit,
|
|
132
|
-
next: "Choose a lead_group_id, then use get_leads, get_context_workspace, or
|
|
132
|
+
next: "Choose a lead_group_id, then use get_leads, get_context_workspace, or list_persona_contracts.",
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
135
|
catch (error) {
|
package/dist/tools/schema.d.ts
CHANGED
|
@@ -1,5 +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">;
|
|
3
|
+
type SchemaClient = Pick<LeadifyClient, "get" | "post" | "rpc">;
|
|
4
4
|
export declare function registerSchemaTools(server: McpServer, client?: SchemaClient): void;
|
|
5
5
|
export {};
|
package/dist/tools/schema.js
CHANGED
|
@@ -78,6 +78,42 @@ export function registerSchemaTools(server, client) {
|
|
|
78
78
|
return handleToolError(error);
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
|
+
// ── schema_migration_preview / apply_schema_migration ─────────────────
|
|
82
|
+
server.tool("schema_migration_preview", "Preview the exact hash-pinned schema migration required to make a lead group READY. " +
|
|
83
|
+
"This is read-only. Review the returned expectedSchemaHash, planHash, impacted leads and " +
|
|
84
|
+
"conversion actions before calling apply_schema_migration.", {
|
|
85
|
+
lead_group_id: z.string().describe("ID of the lead group to inspect."),
|
|
86
|
+
}, async ({ lead_group_id }) => {
|
|
87
|
+
try {
|
|
88
|
+
const data = await (client ?? getClient()).rpc("leadGroups/schemaMigrationPreview", { leadGroupId: lead_group_id });
|
|
89
|
+
return toolResult(data);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return handleToolError(error);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
server.tool("apply_schema_migration", "Apply a destructive schema migration only after schema_migration_preview has been read and " +
|
|
96
|
+
"the exact expected_schema_hash and expected_plan_hash have been supplied. This API performs " +
|
|
97
|
+
"its own optimistic hash checks. Use only for the reviewed lead group; reread its leads and " +
|
|
98
|
+
"schema readiness afterwards.", {
|
|
99
|
+
lead_group_id: z.string().describe("ID of the reviewed lead group."),
|
|
100
|
+
expected_schema_hash: z.string().regex(/^[a-f0-9]{64}$/).describe("expectedSchemaHash from the fresh preview."),
|
|
101
|
+
expected_plan_hash: z.string().regex(/^[a-f0-9]{64}$/).describe("planHash from the same fresh preview."),
|
|
102
|
+
confirmation: z.literal("apply_destructive_schema_migration").describe("Exact acknowledgement required by the server."),
|
|
103
|
+
}, async ({ lead_group_id, expected_schema_hash, expected_plan_hash, confirmation }) => {
|
|
104
|
+
try {
|
|
105
|
+
const data = await (client ?? getClient()).rpc("leadGroups/applySchemaMigration", {
|
|
106
|
+
leadGroupId: lead_group_id,
|
|
107
|
+
expectedSchemaHash: expected_schema_hash,
|
|
108
|
+
expectedPlanHash: expected_plan_hash,
|
|
109
|
+
confirmation,
|
|
110
|
+
});
|
|
111
|
+
return toolResult(data);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return handleToolError(error);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
81
117
|
// ── delete_columns ─────────────────────────────────────────────────────
|
|
82
118
|
server.tool("delete_columns", "Remove one or more columns (fields) from a lead group's schema and all lead data " +
|
|
83
119
|
"stored in those fields. This action is irreversible — field definitions and values " +
|