@agifyai/leadify-mcp 8.5.4 → 8.5.5
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 +6 -6
- package/dist/tools/campaigns.js +56 -17
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -245,12 +245,12 @@ Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
|
245
245
|
| `get_campaign_logs` | Récupérer les logs de campagne avec filtres et pagination. |
|
|
246
246
|
| `delete_campaign_log` | Supprimer une entrée de log de campagne. |
|
|
247
247
|
| `update_campaign_stats` | Mettre à jour les statistiques d'email d'une campagne pour un groupe de leads. |
|
|
248
|
-
| `create_campaign` | Créer une campagne DRAFT mono-canal rattachée à un groupe de leads. `channel` (`LINKEDIN` ou `EMAIL`) est obligatoire ; `start_at
|
|
249
|
-
| `update_campaign_configuration` |
|
|
250
|
-
| `list_campaigns` | Lister compactement les campagnes d'une organisation explicitement sélectionnée
|
|
251
|
-
| `get_campaign` | Récupérer les détails d'une campagne et ses KPIs temps réel. |
|
|
252
|
-
| `update_campaign_status` | Changer le statut d'une campagne
|
|
253
|
-
| `export_campaign` | Exporter les statistiques
|
|
248
|
+
| `create_campaign` | Créer une campagne DRAFT mono-canal rattachée à un groupe de leads. `channel` (`LINKEDIN` ou `EMAIL`) est obligatoire ; `start_at` inclusif, `end_at` exclusif et `timezone` IANA configurent la fenêtre métier. À `end_at`, Leadify met la campagne en pause réversible (`WINDOW_END`) sans la clôturer. |
|
|
249
|
+
| `update_campaign_configuration` | Modifier la fenêtre de toute campagne encore `OPEN`; le canal reste modifiable uniquement en DRAFT. Prolonger `end_at` dans le futur ou le supprimer relance automatiquement une pause `WINDOW_END` après validation du fournisseur, mais jamais une pause manuelle. La fenêtre demandée reste enregistrée si cette validation échoue. |
|
|
250
|
+
| `list_campaigns` | Lister compactement les campagnes d'une organisation explicitement sélectionnée, avec état effectif, fenêtre, motif de pause et clôture. |
|
|
251
|
+
| `get_campaign` | Récupérer les détails d'une campagne, son état effectif, sa clôture et son éventuel rapport final figé, ainsi que ses KPIs temps réel. |
|
|
252
|
+
| `update_campaign_status` | Changer le statut d'une campagne. `PAUSED` est une pause manuelle réversible ; `COMPLETED` déclenche la clôture définitive terminale et son rapport final immuable. |
|
|
253
|
+
| `export_campaign` | Exporter les statistiques en CSV ; après clôture définitive, l'export provient du rapport final figé. |
|
|
254
254
|
| `signal_upsert` | Créer ou mettre à jour un signal de business intelligence (INFO, CRITICAL, GOLDEN). Remet le state à `active`. |
|
|
255
255
|
| `signal_expire` | Expirer un signal (événement périmé). Flip de `state` uniquement. |
|
|
256
256
|
| `signal_disable` | Désactiver un signal (faux positif / écarté manuellement). Flip de `state` uniquement. |
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -2,6 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
import { getClient } from "../client.js";
|
|
3
3
|
import { toolResult, handleToolError } from "../types.js";
|
|
4
4
|
const CAMPAIGN_STATUSES = ["DRAFT", "ACTIVE", "PAUSED", "COMPLETED"];
|
|
5
|
+
const CAMPAIGN_EFFECTIVE_STATES = [
|
|
6
|
+
"DRAFT",
|
|
7
|
+
"SCHEDULED",
|
|
8
|
+
"ACTIVE",
|
|
9
|
+
"PAUSED_MANUAL",
|
|
10
|
+
"PAUSED_WINDOW_END",
|
|
11
|
+
"CLOSED",
|
|
12
|
+
];
|
|
5
13
|
function assertCampaignBelongsToOrganization(data, organizationId) {
|
|
6
14
|
if (!data || typeof data !== "object") {
|
|
7
15
|
throw new Error("Leadify returned an invalid campaign response");
|
|
@@ -89,13 +97,14 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
89
97
|
});
|
|
90
98
|
// ── create_campaign ────────────────────────────────────────────────────
|
|
91
99
|
server.tool("create_campaign", "Create a mono-channel DRAFT campaign with an explicit channel, optional ISO-8601 start/end " +
|
|
92
|
-
"window, and IANA timezone.
|
|
93
|
-
"
|
|
100
|
+
"window, and IANA timezone. startAt is inclusive and endAt is exclusive. Reaching endAt " +
|
|
101
|
+
"reversibly pauses the campaign with reason WINDOW_END while closure remains OPEN; it never " +
|
|
102
|
+
"creates the permanent closure report.", {
|
|
94
103
|
name: z.string().describe("Campaign name."),
|
|
95
104
|
lead_group_id: z.string().describe("ID of the lead group to associate."),
|
|
96
105
|
channel: z.enum(["LINKEDIN", "EMAIL"]).describe("Required effective delivery channel. Create separate campaigns for LinkedIn and email."),
|
|
97
106
|
start_at: z.string().datetime().optional().describe("Optional inclusive campaign start timestamp in ISO-8601 UTC."),
|
|
98
|
-
end_at: z.string().datetime().optional().describe("Optional exclusive campaign end timestamp in ISO-8601 UTC; must be after start_at."),
|
|
107
|
+
end_at: z.string().datetime().optional().describe("Optional exclusive campaign end timestamp in ISO-8601 UTC; must be after start_at. Reaching it pauses, but does not close, the campaign."),
|
|
99
108
|
timezone: z.string().min(1).optional().describe("IANA timezone used for the campaign business window, for example Europe/Paris."),
|
|
100
109
|
description: z.string().optional().describe("Optional description."),
|
|
101
110
|
persona_id: z
|
|
@@ -126,12 +135,16 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
126
135
|
return handleToolError(error);
|
|
127
136
|
}
|
|
128
137
|
});
|
|
129
|
-
server.tool("update_campaign_configuration", "
|
|
138
|
+
server.tool("update_campaign_configuration", "Edit the lifecycle window of any OPEN campaign; changing channel remains limited to DRAFT. " +
|
|
139
|
+
"Extending endAt into the future or removing it automatically resumes only a campaign paused " +
|
|
140
|
+
"because WINDOW_END, after provider validation and immediate replanning. A MANUAL pause never " +
|
|
141
|
+
"auto-resumes. The requested window remains saved when provider validation fails, and a CLOSED " +
|
|
142
|
+
"campaign is terminal.", {
|
|
130
143
|
id: z.string().describe("Campaign ID."),
|
|
131
|
-
channel: z.enum(["LINKEDIN", "EMAIL"]).optional(),
|
|
132
|
-
start_at: z.string().datetime().nullable().optional(),
|
|
133
|
-
end_at: z.string().datetime().nullable().optional(),
|
|
134
|
-
timezone: z.string().min(1).optional(),
|
|
144
|
+
channel: z.enum(["LINKEDIN", "EMAIL"]).optional().describe("Delivery channel; mutable only while status is DRAFT."),
|
|
145
|
+
start_at: z.string().datetime().nullable().optional().describe("Inclusive UTC start timestamp, or null to remove the lower bound."),
|
|
146
|
+
end_at: z.string().datetime().nullable().optional().describe("Exclusive UTC end timestamp, or null to remove the upper bound. A future value or null can auto-resume only a WINDOW_END pause."),
|
|
147
|
+
timezone: z.string().min(1).optional().describe("IANA timezone used for the business window, for example Europe/Paris."),
|
|
135
148
|
}, async ({ id, channel, start_at, end_at, timezone }) => {
|
|
136
149
|
try {
|
|
137
150
|
const body = {};
|
|
@@ -151,8 +164,9 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
151
164
|
});
|
|
152
165
|
// ── get_campaign ───────────────────────────────────────────────────────
|
|
153
166
|
server.tool("get_campaign", "Retrieve a campaign chosen from list_campaigns, scoped to the explicitly selected " +
|
|
154
|
-
"organization. Includes
|
|
155
|
-
"and real-time KPIs. Never use
|
|
167
|
+
"organization. Includes status, effectiveState, pauseReason, pausedAt, window, terminal " +
|
|
168
|
+
"closure metadata, immutable final report when present, and real-time KPIs. Never use " +
|
|
169
|
+
"campaign logs as the source of lifecycle state.", {
|
|
156
170
|
id: z.string().describe("Campaign ID."),
|
|
157
171
|
organization_id: z.string().describe("Organization selected explicitly via discover_leadify_context or list_organizations."),
|
|
158
172
|
}, async ({ id, organization_id }) => {
|
|
@@ -165,9 +179,10 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
165
179
|
}
|
|
166
180
|
});
|
|
167
181
|
// ── list_campaigns ─────────────────────────────────────────────────────
|
|
168
|
-
server.tool("list_campaigns", "List campaigns in one explicitly selected organization with compact
|
|
169
|
-
"
|
|
170
|
-
"
|
|
182
|
+
server.tool("list_campaigns", "List campaigns in one explicitly selected organization with compact persisted and effective " +
|
|
183
|
+
"lifecycle metadata. Use effectiveState to distinguish SCHEDULED, ACTIVE, a manual pause, an " +
|
|
184
|
+
"end-of-window pause, and permanent closure without reading historical logs. Fetch get_campaign " +
|
|
185
|
+
"for the immutable final report.", {
|
|
171
186
|
organization_id: z.string().describe("Organization selected explicitly via discover_leadify_context or list_organizations."),
|
|
172
187
|
status: z.enum(CAMPAIGN_STATUSES).optional().describe("Optional current-status filter."),
|
|
173
188
|
limit: z.number().int().min(1).max(100).optional().default(50).describe("Maximum campaigns returned, bounded to 100."),
|
|
@@ -188,24 +203,46 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
188
203
|
if (!CAMPAIGN_STATUSES.includes(campaign.status)) {
|
|
189
204
|
throw new Error("Leadify returned a campaign with an invalid current status");
|
|
190
205
|
}
|
|
206
|
+
if (!CAMPAIGN_EFFECTIVE_STATES.includes(campaign.effectiveState)) {
|
|
207
|
+
throw new Error("Leadify returned a campaign with an invalid effective state");
|
|
208
|
+
}
|
|
209
|
+
const closure = campaign.closure;
|
|
210
|
+
if (!closure || typeof closure !== "object") {
|
|
211
|
+
throw new Error("Leadify returned a campaign without closure metadata");
|
|
212
|
+
}
|
|
213
|
+
const closureRecord = closure;
|
|
191
214
|
return {
|
|
192
215
|
id: campaign.id,
|
|
193
216
|
name: campaign.name,
|
|
194
217
|
status: campaign.status,
|
|
218
|
+
effectiveState: campaign.effectiveState,
|
|
195
219
|
channel: campaign.channel,
|
|
196
220
|
leadGroupId: campaign.leadGroupId,
|
|
221
|
+
startAt: campaign.startAt,
|
|
222
|
+
endAt: campaign.endAt,
|
|
223
|
+
timezone: campaign.timezone,
|
|
224
|
+
pauseReason: campaign.pauseReason,
|
|
225
|
+
pausedAt: campaign.pausedAt,
|
|
226
|
+
closure: {
|
|
227
|
+
state: closureRecord.state,
|
|
228
|
+
closedAt: closureRecord.closedAt,
|
|
229
|
+
terminal: closureRecord.terminal,
|
|
230
|
+
reportAvailable: closureRecord.report !== null && closureRecord.report !== undefined,
|
|
231
|
+
},
|
|
197
232
|
createdAt: campaign.createdAt,
|
|
198
233
|
updatedAt: campaign.updatedAt,
|
|
199
234
|
};
|
|
200
235
|
});
|
|
201
236
|
const filtered = status ? campaigns.filter((campaign) => campaign.status === status) : campaigns;
|
|
202
237
|
const statusCounts = Object.fromEntries(CAMPAIGN_STATUSES.map((value) => [value, campaigns.filter((campaign) => campaign.status === value).length]));
|
|
238
|
+
const effectiveStateCounts = Object.fromEntries(CAMPAIGN_EFFECTIVE_STATES.map((value) => [value, campaigns.filter((campaign) => campaign.effectiveState === value).length]));
|
|
203
239
|
return toolResult({
|
|
204
240
|
organizationId: organization_id,
|
|
205
241
|
campaigns: filtered.slice(0, limit),
|
|
206
242
|
total: filtered.length,
|
|
207
243
|
truncated: filtered.length > limit,
|
|
208
244
|
statusCounts,
|
|
245
|
+
effectiveStateCounts,
|
|
209
246
|
});
|
|
210
247
|
}
|
|
211
248
|
catch (error) {
|
|
@@ -214,8 +251,9 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
214
251
|
});
|
|
215
252
|
// ── update_campaign_status ─────────────────────────────────────────────
|
|
216
253
|
server.tool("update_campaign_status", "Change a campaign's workflow status. Allowed values: DRAFT, ACTIVE, PAUSED, COMPLETED. " +
|
|
217
|
-
"Moving to ACTIVE starts outbound execution; PAUSED
|
|
218
|
-
"
|
|
254
|
+
"Moving to ACTIVE starts outbound execution; PAUSED is a reversible manual pause; COMPLETED " +
|
|
255
|
+
"means explicit permanent closure and creates the immutable final report. Permanent closure is " +
|
|
256
|
+
"terminal and cannot be reopened. Always confirm with the user before calling this tool.", {
|
|
219
257
|
id: z.string().describe("Campaign ID."),
|
|
220
258
|
status: z
|
|
221
259
|
.enum(["DRAFT", "ACTIVE", "PAUSED", "COMPLETED"])
|
|
@@ -230,8 +268,9 @@ export function registerCampaignTools(server, readClient, writeClient, mutationC
|
|
|
230
268
|
}
|
|
231
269
|
});
|
|
232
270
|
// ── export_campaign ────────────────────────────────────────────────────
|
|
233
|
-
server.tool("export_campaign", "Export a campaign's
|
|
234
|
-
"
|
|
271
|
+
server.tool("export_campaign", "Export a campaign's statistics and metrics as CSV. A permanently closed campaign is exported " +
|
|
272
|
+
"from its immutable final report, so late replies remain visible in current activity without " +
|
|
273
|
+
"rewriting the export. Returns the raw CSV text.", {
|
|
235
274
|
id: z.string().describe("Campaign ID."),
|
|
236
275
|
}, async ({ id }) => {
|
|
237
276
|
try {
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED