@agifyai/leadify-mcp 1.5.2 → 2.0.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.
@@ -163,8 +163,11 @@ const DOCUMENT_CATEGORIES = [
163
163
  "OTHER",
164
164
  ];
165
165
  // ─── Common descriptions ───────────────────────────────────────────────────
166
- const ORG_ID_DESC = "Optional Clerk organization ID to target a specific org's data room. " +
167
- "Defaults to the caller's own org. Requires admin access for writes.";
166
+ const ORG_ID_DESC = "REQUIRED Clerk organization ID targeting the org whose data room you act on. " +
167
+ "Resolve it from the lead group you are working on (e.g. via list_organizations) " +
168
+ "and pass it explicitly. NEVER omit it: there is no safe default — the API key " +
169
+ "spans multiple orgs and an empty value would silently hit the wrong company's " +
170
+ "data room (read or write). Requires admin access for writes.";
168
171
  const DRY_RUN_DESC = "If true, validate the merged payload + return the preview WITHOUT writing. " +
169
172
  "Use to dry-fit a change before persisting.";
170
173
  async function fetchDataRoom(organizationId) {
@@ -242,7 +245,7 @@ export function registerDataRoomTools(server) {
242
245
  server.tool("get_data_room", "Retrieve the organization's full data room: company info (v2 schema), all documents, and " +
243
246
  "every persona defined for the workspace. Use this as the entry point when you need to " +
244
247
  "understand the company's context before writing campaigns or messages.", {
245
- organization_id: z.string().optional().describe(ORG_ID_DESC),
248
+ organization_id: z.string().describe(ORG_ID_DESC),
246
249
  }, async ({ organization_id }) => {
247
250
  try {
248
251
  const data = await fetchDataRoom(organization_id);
@@ -361,7 +364,7 @@ export function registerDataRoomTools(server) {
361
364
  company_info: z
362
365
  .record(z.unknown())
363
366
  .describe("Full v2 companyInfo payload (replaces the existing one). See describe_company_info_schema for the contract."),
364
- organization_id: z.string().optional().describe(ORG_ID_DESC),
367
+ organization_id: z.string().describe(ORG_ID_DESC),
365
368
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
366
369
  }, async ({ company_info, organization_id, dry_run }) => {
367
370
  try {
@@ -392,7 +395,7 @@ export function registerDataRoomTools(server) {
392
395
  .max(CHAR_LIMITS.website)
393
396
  .optional()
394
397
  .describe("Company website URL (max 500)."),
395
- organization_id: z.string().optional().describe(ORG_ID_DESC),
398
+ organization_id: z.string().describe(ORG_ID_DESC),
396
399
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
397
400
  }, async ({ name, website, organization_id, dry_run }) => {
398
401
  try {
@@ -452,7 +455,7 @@ export function registerDataRoomTools(server) {
452
455
  .max(10)
453
456
  .optional()
454
457
  .describe("Replaces constraints[] wholesale. For per-item edits use update_company_info_constraint."),
455
- organization_id: z.string().optional().describe(ORG_ID_DESC),
458
+ organization_id: z.string().describe(ORG_ID_DESC),
456
459
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
457
460
  }, async ({ pitchOneLiner, marketSpecialty, stage, salesTeamSizeFR, geo, constraints, organization_id, dry_run, }) => {
458
461
  try {
@@ -511,7 +514,7 @@ export function registerDataRoomTools(server) {
511
514
  constraint: constraintSchema
512
515
  .optional()
513
516
  .describe("Constraint payload {label ≤150, type ∈ regulatory|brand|legal, note? ≤150}. Required for 'add' and 'replace'."),
514
- organization_id: z.string().optional().describe(ORG_ID_DESC),
517
+ organization_id: z.string().describe(ORG_ID_DESC),
515
518
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
516
519
  }, async ({ action, index, constraint, organization_id, dry_run }) => {
517
520
  try {
@@ -576,7 +579,7 @@ export function registerDataRoomTools(server) {
576
579
  product: productSchema
577
580
  .optional()
578
581
  .describe("Product payload. Required for 'add' and 'replace'."),
579
- organization_id: z.string().optional().describe(ORG_ID_DESC),
582
+ organization_id: z.string().describe(ORG_ID_DESC),
580
583
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
581
584
  }, async ({ action, name, product, organization_id, dry_run }) => {
582
585
  try {
@@ -656,7 +659,7 @@ export function registerDataRoomTools(server) {
656
659
  .max(CHAR_LIMITS.defaultBaseline)
657
660
  .optional()
658
661
  .describe("Default baseline (max 200)."),
659
- organization_id: z.string().optional().describe(ORG_ID_DESC),
662
+ organization_id: z.string().describe(ORG_ID_DESC),
660
663
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
661
664
  }, async ({ pricingModel, ticketRange, salesCycleMonths, triggers, defaultBaseline, organization_id, dry_run, }) => {
662
665
  try {
@@ -704,7 +707,7 @@ export function registerDataRoomTools(server) {
704
707
  icp: productICPSchema
705
708
  .optional()
706
709
  .describe("ICP payload. Required for 'add' and 'replace'."),
707
- organization_id: z.string().optional().describe(ORG_ID_DESC),
710
+ organization_id: z.string().describe(ORG_ID_DESC),
708
711
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
709
712
  }, async ({ action, productId, icp, organization_id, dry_run }) => {
710
713
  try {
@@ -769,7 +772,7 @@ export function registerDataRoomTools(server) {
769
772
  .array(z.string().max(CHAR_LIMITS.forbiddenWord))
770
773
  .optional()
771
774
  .describe("Replaces forbiddenWords[] wholesale (each ≤100)."),
772
- organization_id: z.string().optional().describe(ORG_ID_DESC),
775
+ organization_id: z.string().describe(ORG_ID_DESC),
773
776
  dry_run: z.boolean().optional().describe(DRY_RUN_DESC),
774
777
  }, async ({ keyMetrics, miniStories, forbiddenWords, organization_id, dry_run }) => {
775
778
  try {
@@ -810,7 +813,7 @@ export function registerDataRoomTools(server) {
810
813
  .string()
811
814
  .optional()
812
815
  .describe("Text or markdown content."),
813
- organization_id: z.string().optional().describe(ORG_ID_DESC),
816
+ organization_id: z.string().describe(ORG_ID_DESC),
814
817
  }, async ({ title, category, content, organization_id }) => {
815
818
  try {
816
819
  const body = { title, category };
@@ -62,12 +62,9 @@ export function registerLeadTools(server) {
62
62
  .number()
63
63
  .int()
64
64
  .positive()
65
+ .max(50)
65
66
  .optional()
66
- .describe("Results per page. Defaults to 50."),
67
- no_pagination: z
68
- .boolean()
69
- .optional()
70
- .describe("If true, returns ALL leads in the group without pagination. Use with caution on large groups."),
67
+ .describe("Results per page. Defaults to 50, hard-capped at 50."),
71
68
  search: z
72
69
  .string()
73
70
  .optional()
@@ -79,7 +76,7 @@ export function registerLeadTools(server) {
79
76
  "operator to value. Operators: equals, not_equals, contains, not_contains, " +
80
77
  "gte, lte, is_true, is_false, is_null, is_not_null. " +
81
78
  'Example: {"industry": {"contains": "tech"}, "company": {"is_not_null": "true"}}'),
82
- }, async ({ group_id, view_id, page, limit, no_pagination, search, filters }) => {
79
+ }, async ({ group_id, view_id, page, limit, search, filters }) => {
83
80
  try {
84
81
  const params = new URLSearchParams();
85
82
  params.set("groupId", group_id);
@@ -89,8 +86,6 @@ export function registerLeadTools(server) {
89
86
  params.set("page", String(page));
90
87
  if (limit !== undefined)
91
88
  params.set("limit", String(limit));
92
- if (no_pagination)
93
- params.set("noPagination", "true");
94
89
  if (search)
95
90
  params.set("search", search);
96
91
  if (filters) {
@@ -3,10 +3,16 @@ import { getClient } from "../client.js";
3
3
  import { toolResult, handleToolError } from "../types.js";
4
4
  export function registerPipelineTools(server) {
5
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. " +
6
+ server.tool("trigger_outreach", "Generate the outreach message SEQUENCE for a lead via the Trigger.dev agent " +
7
+ "(Leadify v2 writer). The agent is a PURE MESSAGE WRITER: it produces a multi-touch " +
8
+ "sequence (card_connexion, card_linkedin_one/two/three, card_email_one/two/three plus " +
9
+ "their subjects), filtered to the channels the lead can actually receive. It does NOT " +
10
+ "score, tier, analyse or research — there is no card_analysis/card_research/score/tier " +
11
+ "in the output (those belong to qualification, not writing). " +
12
+ "DRY-RUN BY DEFAULT (dryrun=true) — the agent runs identically but NEVER persists " +
13
+ "anything to the DB; the generated cards travel back in the tool output so you can " +
14
+ "iterate without polluting the DB. Use dryrun=false for production writes (persists the " +
15
+ "card_* sequence fields to lead.data). " +
10
16
  "The 'force' flag (default false) bypasses the 'already has a message?' guard — " +
11
17
  "useful for re-generating on an already-processed lead. " +
12
18
  "Waits up to 120s for the remote agent to complete.", {
@@ -15,8 +21,9 @@ export function registerPipelineTools(server) {
15
21
  .boolean()
16
22
  .optional()
17
23
  .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."),
24
+ .describe("true (default) = dry-run mode: agent runs but writes nothing to DB (generated " +
25
+ "cards returned in the tool output). " +
26
+ "false = production mode: agent persists the card_* sequence fields to lead.data."),
20
27
  force: z
21
28
  .boolean()
22
29
  .optional()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agifyai/leadify-mcp",
3
- "version": "1.5.2",
3
+ "version": "2.0.0",
4
4
  "description": "MCP server for Leadify lead management API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",