@agifyai/leadify-mcp 1.5.0 → 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.
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerPipelineTools(server: McpServer): void;
@@ -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
+ }
package/dist/types.js CHANGED
@@ -29,6 +29,17 @@ export function toolError(message, details) {
29
29
  }
30
30
  export function handleToolError(error) {
31
31
  if (error instanceof LeadifyApiError) {
32
+ // If the backend returned a cumulative ZodIssue list (formatZodError now
33
+ // emits `issues[]` on every 422), surface the count up-front in the
34
+ // message so the caller can fix every field in a single retry instead
35
+ // of looping one-error-at-a-time on the legacy `error` field.
36
+ const body = error.responseBody;
37
+ if (body && typeof body === "object" && !Array.isArray(body) && "issues" in body) {
38
+ const issues = body.issues;
39
+ if (Array.isArray(issues) && issues.length > 1) {
40
+ return toolError(`Validation failed: ${issues.length} issues. See details.response.issues[] for every path that needs fixing.`, { statusCode: error.statusCode, response: body });
41
+ }
42
+ }
32
43
  return toolError(error.message, {
33
44
  statusCode: error.statusCode,
34
45
  response: error.responseBody,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agifyai/leadify-mcp",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "MCP server for Leadify lead management API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",