@agifyai/leadify-mcp 8.0.0 → 8.1.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 +1 -0
- package/dist/server.js +1 -1
- package/dist/tools/campaigns.d.ts +4 -1
- package/dist/tools/campaigns.js +74 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -192,6 +192,7 @@ Conséquences pratiques :
|
|
|
192
192
|
| `delete_campaign_log` | Supprimer une entrée de log de campagne. |
|
|
193
193
|
| `update_campaign_stats` | Mettre à jour les statistiques d'email d'une campagne pour un groupe de leads. |
|
|
194
194
|
| `create_campaign` | Créer une nouvelle campagne rattachée à un groupe de leads (statut DRAFT). |
|
|
195
|
+
| `list_campaigns` | Lister compactement les campagnes d'une organisation explicitement sélectionnée et leur statut courant. |
|
|
195
196
|
| `get_campaign` | Récupérer les détails d'une campagne et ses KPIs temps réel. |
|
|
196
197
|
| `update_campaign_status` | Changer le statut d'une campagne (DRAFT, ACTIVE, PAUSED, COMPLETED). |
|
|
197
198
|
| `export_campaign` | Exporter les statistiques complètes d'une campagne en CSV. |
|
package/dist/server.js
CHANGED
|
@@ -16,7 +16,7 @@ import { registerContextWorkspaceTools } from "./tools/context_workspace.js";
|
|
|
16
16
|
export function createServer() {
|
|
17
17
|
const server = new McpServer({
|
|
18
18
|
name: "leadify",
|
|
19
|
-
version: "8.
|
|
19
|
+
version: "8.1.0",
|
|
20
20
|
});
|
|
21
21
|
registerAuthTools(server);
|
|
22
22
|
registerOrganizationTools(server);
|
|
@@ -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 CampaignReadClient = Pick<LeadifyClient, "get">;
|
|
4
|
+
export declare function registerCampaignTools(server: McpServer, readClient?: CampaignReadClient): void;
|
|
5
|
+
export {};
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
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
|
+
function assertCampaignBelongsToOrganization(data, organizationId) {
|
|
6
|
+
if (!data || typeof data !== "object") {
|
|
7
|
+
throw new Error("Leadify returned an invalid campaign response");
|
|
8
|
+
}
|
|
9
|
+
const campaign = data;
|
|
10
|
+
const leadGroup = campaign.leadGroup;
|
|
11
|
+
const actualOrganizationId = campaign.organizationId ?? (leadGroup && typeof leadGroup === "object"
|
|
12
|
+
? leadGroup.organizationId
|
|
13
|
+
: undefined);
|
|
14
|
+
if (actualOrganizationId !== organizationId) {
|
|
15
|
+
throw new Error("Campaign does not belong to the explicitly selected organization");
|
|
16
|
+
}
|
|
17
|
+
if (!CAMPAIGN_STATUSES.includes(campaign.status)) {
|
|
18
|
+
throw new Error("Leadify returned a campaign with an invalid current status");
|
|
19
|
+
}
|
|
20
|
+
return campaign;
|
|
21
|
+
}
|
|
22
|
+
export function registerCampaignTools(server, readClient) {
|
|
5
23
|
// ── update_campaign_stats ──────────────────────────────────────────────
|
|
6
24
|
server.tool("update_campaign_stats", "Update email campaign statistics for a lead group. If the given campaignSlug " +
|
|
7
25
|
"doesn't exist in the group's stats yet, it is created; otherwise provided fields " +
|
|
@@ -98,14 +116,63 @@ export function registerCampaignTools(server) {
|
|
|
98
116
|
}
|
|
99
117
|
});
|
|
100
118
|
// ── get_campaign ───────────────────────────────────────────────────────
|
|
101
|
-
server.tool("get_campaign", "Retrieve
|
|
102
|
-
"
|
|
103
|
-
"
|
|
119
|
+
server.tool("get_campaign", "Retrieve a campaign chosen from list_campaigns, scoped to the explicitly selected " +
|
|
120
|
+
"organization. Includes its normalized current status (DRAFT, ACTIVE, PAUSED, or COMPLETED) " +
|
|
121
|
+
"and real-time KPIs. Never use campaign logs as the source of status.", {
|
|
104
122
|
id: z.string().describe("Campaign ID."),
|
|
105
|
-
|
|
123
|
+
organization_id: z.string().describe("Organization selected explicitly via discover_leadify_context or list_organizations."),
|
|
124
|
+
}, async ({ id, organization_id }) => {
|
|
106
125
|
try {
|
|
107
|
-
const data = await getClient().get(`/api/campaign/${encodeURIComponent(id)}`);
|
|
108
|
-
return toolResult(data);
|
|
126
|
+
const data = await (readClient ?? getClient()).get(`/api/campaign/${encodeURIComponent(id)}`);
|
|
127
|
+
return toolResult(assertCampaignBelongsToOrganization(data, organization_id));
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return handleToolError(error);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
// ── list_campaigns ─────────────────────────────────────────────────────
|
|
134
|
+
server.tool("list_campaigns", "List campaigns in one explicitly selected organization with compact current status metadata. " +
|
|
135
|
+
"Use this after discover_leadify_context to identify ACTIVE, DRAFT, PAUSED, or COMPLETED " +
|
|
136
|
+
"campaigns without reading historical logs.", {
|
|
137
|
+
organization_id: z.string().describe("Organization selected explicitly via discover_leadify_context or list_organizations."),
|
|
138
|
+
status: z.enum(CAMPAIGN_STATUSES).optional().describe("Optional current-status filter."),
|
|
139
|
+
limit: z.number().int().min(1).max(100).optional().default(50).describe("Maximum campaigns returned, bounded to 100."),
|
|
140
|
+
}, async ({ organization_id, status, limit }) => {
|
|
141
|
+
try {
|
|
142
|
+
const data = await (readClient ?? getClient()).get("/api/campaigns", new URLSearchParams({ organizationId: organization_id }));
|
|
143
|
+
if (!data || typeof data !== "object" || !Array.isArray(data.campaigns)) {
|
|
144
|
+
throw new Error("Leadify returned an invalid campaign list response");
|
|
145
|
+
}
|
|
146
|
+
const response = data;
|
|
147
|
+
if (response.organizationId !== organization_id) {
|
|
148
|
+
throw new Error("Leadify returned campaigns from a different organization");
|
|
149
|
+
}
|
|
150
|
+
const campaigns = response.campaigns.map((item) => {
|
|
151
|
+
if (!item || typeof item !== "object")
|
|
152
|
+
throw new Error("Leadify returned an invalid campaign");
|
|
153
|
+
const campaign = item;
|
|
154
|
+
if (!CAMPAIGN_STATUSES.includes(campaign.status)) {
|
|
155
|
+
throw new Error("Leadify returned a campaign with an invalid current status");
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
id: campaign.id,
|
|
159
|
+
name: campaign.name,
|
|
160
|
+
status: campaign.status,
|
|
161
|
+
channel: campaign.channel,
|
|
162
|
+
leadGroupId: campaign.leadGroupId,
|
|
163
|
+
createdAt: campaign.createdAt,
|
|
164
|
+
updatedAt: campaign.updatedAt,
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
const filtered = status ? campaigns.filter((campaign) => campaign.status === status) : campaigns;
|
|
168
|
+
const statusCounts = Object.fromEntries(CAMPAIGN_STATUSES.map((value) => [value, campaigns.filter((campaign) => campaign.status === value).length]));
|
|
169
|
+
return toolResult({
|
|
170
|
+
organizationId: organization_id,
|
|
171
|
+
campaigns: filtered.slice(0, limit),
|
|
172
|
+
total: filtered.length,
|
|
173
|
+
truncated: filtered.length > limit,
|
|
174
|
+
statusCounts,
|
|
175
|
+
});
|
|
109
176
|
}
|
|
110
177
|
catch (error) {
|
|
111
178
|
return handleToolError(error);
|