@agifyai/leadify-mcp 1.5.1 → 1.5.2
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 -0
- package/dist/server.js +2 -0
- package/dist/tools/pipeline.d.ts +2 -0
- package/dist/tools/pipeline.js +104 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -223,3 +223,6 @@ Conséquences pratiques :
|
|
|
223
223
|
| `update_outreach_case_study` | Ajouter / remplacer / supprimer un case study par index, sans re-envoyer la liste. |
|
|
224
224
|
| `update_outreach_urls` | Patch booking_url et/ou website_url uniquement. |
|
|
225
225
|
| `set_outreach_connection_request` | Toggle du flag connectionRequestEnabled (LinkedIn invite vs cold DM). |
|
|
226
|
+
| `trigger_outreach` | Générer un message d'outreach via l'agent Trigger.dev (dry-run par défaut). `force=true` pour re-générer sur un lead déjà traité. |
|
|
227
|
+
| `append_fine_tuning` | Appendre du contenu à une section du Fine Tuning (nonNegotiableRules, pitfalls, structure, examples). Toujours en mode append — garantie contractuelle. |
|
|
228
|
+
| `pipeline_next_lead` | Sélectionner le prochain lead à traiter (score descendant, sans message). Exclusion des IDs déjà vus, limit 1-5. |
|
package/dist/server.js
CHANGED
|
@@ -12,6 +12,7 @@ import { registerPersonaTools } from "./tools/personas.js";
|
|
|
12
12
|
import { registerLeadGroupTools } from "./tools/lead_groups.js";
|
|
13
13
|
import { registerOrganizationTools } from "./tools/organizations.js";
|
|
14
14
|
import { registerOutreachSettingsTools } from "./tools/outreach_settings.js";
|
|
15
|
+
import { registerPipelineTools } from "./tools/pipeline.js";
|
|
15
16
|
export function createServer() {
|
|
16
17
|
const server = new McpServer({
|
|
17
18
|
name: "leadify",
|
|
@@ -30,5 +31,6 @@ export function createServer() {
|
|
|
30
31
|
registerDataSourceTools(server);
|
|
31
32
|
registerPersonaTools(server);
|
|
32
33
|
registerOutreachSettingsTools(server);
|
|
34
|
+
registerPipelineTools(server);
|
|
33
35
|
return server;
|
|
34
36
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getClient } from "../client.js";
|
|
3
|
+
import { toolResult, handleToolError } from "../types.js";
|
|
4
|
+
export function registerPipelineTools(server) {
|
|
5
|
+
// ── trigger_outreach ─────────────────────────────────────────────────────
|
|
6
|
+
server.tool("trigger_outreach", "Generate an outreach message for a lead via the Trigger.dev agent. " +
|
|
7
|
+
"DRY-RUN BY DEFAULT (dryrun=true) — the agent runs all logic (analysis, messaging, research) " +
|
|
8
|
+
"but NEVER persists anything to the database. Essential for iterating on messages via the " +
|
|
9
|
+
"MCP pipeline without polluting the DB. Use dryrun=false for production writes. " +
|
|
10
|
+
"The 'force' flag (default false) bypasses the 'already has a message?' guard — " +
|
|
11
|
+
"useful for re-generating on an already-processed lead. " +
|
|
12
|
+
"Waits up to 120s for the remote agent to complete.", {
|
|
13
|
+
lead_id: z.string().describe("ID of the lead to generate a message for."),
|
|
14
|
+
dryrun: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.optional()
|
|
17
|
+
.default(true)
|
|
18
|
+
.describe("true (default) = dry-run mode: agent runs but writes nothing to DB. " +
|
|
19
|
+
"false = production mode: agent writes card_message, card_analysis, etc. to lead.data."),
|
|
20
|
+
force: z
|
|
21
|
+
.boolean()
|
|
22
|
+
.optional()
|
|
23
|
+
.default(false)
|
|
24
|
+
.describe("true = bypass the 'already has a message?' check so you can re-generate. " +
|
|
25
|
+
"false (default) = normal behaviour: skip leads that already have card_message."),
|
|
26
|
+
}, async ({ lead_id, dryrun, force }) => {
|
|
27
|
+
try {
|
|
28
|
+
const data = await getClient().post("/api/trigger-outreach", {
|
|
29
|
+
leadId: lead_id,
|
|
30
|
+
dryrun,
|
|
31
|
+
force,
|
|
32
|
+
});
|
|
33
|
+
return toolResult(data);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return handleToolError(error);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// ── append_fine_tuning ───────────────────────────────────────────────────
|
|
40
|
+
server.tool("append_fine_tuning", "Append content to a Fine Tuning section for a lead group. " +
|
|
41
|
+
"ALWAYS in append mode — NEVER replaces existing content (contractual guarantee). " +
|
|
42
|
+
"Use this to incrementally build up rules and examples as you iterate with the user. " +
|
|
43
|
+
"Sections: nonNegotiableRules (imperative rules), pitfalls (traps to avoid), " +
|
|
44
|
+
"structure (imposed message structure), examples (validated message samples). " +
|
|
45
|
+
"For full rewrites, use the Leadify UI or the classic update endpoint.", {
|
|
46
|
+
lead_group_id: z.string().describe("ID of the lead group."),
|
|
47
|
+
section: z
|
|
48
|
+
.enum(["nonNegotiableRules", "pitfalls", "structure", "examples"])
|
|
49
|
+
.describe("Fine Tuning section to enrich."),
|
|
50
|
+
content: z
|
|
51
|
+
.string()
|
|
52
|
+
.describe("Content to APPEND (not replace). Concatenated after existing content."),
|
|
53
|
+
separator: z
|
|
54
|
+
.string()
|
|
55
|
+
.optional()
|
|
56
|
+
.default("\n\n---\n\n")
|
|
57
|
+
.describe("Separator between old and new content. Default: '\\n\\n---\\n\\n'."),
|
|
58
|
+
}, async ({ lead_group_id, section, content, separator }) => {
|
|
59
|
+
try {
|
|
60
|
+
const data = await getClient().post("/api/pipeline/append-fine-tuning", {
|
|
61
|
+
leadGroupId: lead_group_id,
|
|
62
|
+
section,
|
|
63
|
+
content,
|
|
64
|
+
separator,
|
|
65
|
+
});
|
|
66
|
+
return toolResult(data);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
return handleToolError(error);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
// ── pipeline_next_lead ───────────────────────────────────────────────────
|
|
73
|
+
server.tool("pipeline_next_lead", "Select the next lead to process in the campaign pipeline. " +
|
|
74
|
+
"Returns the hottest leads (highest score) that don't yet have a message " +
|
|
75
|
+
"(card_message empty or missing). Exclude already-processed leads via exclude_lead_ids. " +
|
|
76
|
+
"Call this at the start of each pipeline iteration to get a fresh lead.", {
|
|
77
|
+
lead_group_id: z.string().describe("ID of the lead group to query."),
|
|
78
|
+
exclude_lead_ids: z
|
|
79
|
+
.array(z.string())
|
|
80
|
+
.optional()
|
|
81
|
+
.default([])
|
|
82
|
+
.describe("IDs of leads already processed in this session (to avoid duplicates)."),
|
|
83
|
+
limit: z
|
|
84
|
+
.number()
|
|
85
|
+
.int()
|
|
86
|
+
.min(1)
|
|
87
|
+
.max(5)
|
|
88
|
+
.optional()
|
|
89
|
+
.default(1)
|
|
90
|
+
.describe("Maximum number of leads to return (1-5, default 1)."),
|
|
91
|
+
}, async ({ lead_group_id, exclude_lead_ids, limit }) => {
|
|
92
|
+
try {
|
|
93
|
+
const data = await getClient().post("/api/pipeline/next-lead", {
|
|
94
|
+
leadGroupId: lead_group_id,
|
|
95
|
+
excludeLeadIds: exclude_lead_ids,
|
|
96
|
+
limit,
|
|
97
|
+
});
|
|
98
|
+
return toolResult(data);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return handleToolError(error);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|