@agifyai/leadify-mcp 8.3.9 → 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 +7 -0
- 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/relationships.d.ts +2 -0
- package/dist/tools/relationships.js +124 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -215,6 +215,13 @@ Conséquences pratiques :
|
|
|
215
215
|
| `add_activity` | Journaliser une interaction prospect (LinkedIn, email, call) dans le feed du lead. |
|
|
216
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. |
|
|
217
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. |
|
|
218
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. |
|
|
219
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. |
|
|
220
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. |
|
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
|
|
@@ -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
|
+
}
|