@agifyai/leadify-mcp 1.4.1

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 registerSchemaTools(server: McpServer): void;
@@ -0,0 +1,130 @@
1
+ import { z } from "zod";
2
+ import { getClient } from "../client.js";
3
+ import { toolResult, handleToolError } from "../types.js";
4
+ export function registerSchemaTools(server) {
5
+ // ── update_schema ──────────────────────────────────────────────────────
6
+ server.tool("update_schema", "Add or modify field definitions in a lead group's schema. Each field update " +
7
+ "specifies the internal field name, display label, data type, whether it's required, " +
8
+ "and options for select/multiselect types. Available types: string, textarea, email, " +
9
+ "phone, number, date, boolean, select, multiselect, url. Requires admin permissions. " +
10
+ "Use this to configure how fields appear and behave in the Leadify UI — for example, " +
11
+ "adding a new 'status' dropdown or changing a field's label.", {
12
+ lead_group_id: z
13
+ .string()
14
+ .describe("ID of the lead group whose schema to update."),
15
+ schema_updates: z
16
+ .array(z.object({
17
+ field_name: z
18
+ .string()
19
+ .describe("Internal field name in the schema (e.g. 'status', 'notes')."),
20
+ label: z
21
+ .string()
22
+ .describe("Display label shown in the UI (e.g. 'Statut', 'Notes')."),
23
+ type: z
24
+ .enum([
25
+ "string",
26
+ "textarea",
27
+ "email",
28
+ "phone",
29
+ "number",
30
+ "date",
31
+ "boolean",
32
+ "select",
33
+ "multiselect",
34
+ "url",
35
+ ])
36
+ .describe("Data type of the field."),
37
+ required: z
38
+ .boolean()
39
+ .optional()
40
+ .describe("Whether the field is required when creating/updating leads."),
41
+ options: z
42
+ .array(z.string())
43
+ .optional()
44
+ .describe("List of allowed values for 'select' and 'multiselect' field types. " +
45
+ "Ignored for other types."),
46
+ }))
47
+ .min(1)
48
+ .describe("Array of field definitions to add or update."),
49
+ }, async ({ lead_group_id, schema_updates }) => {
50
+ try {
51
+ const body = {
52
+ leadGroupId: lead_group_id,
53
+ schemaUpdates: schema_updates.map((u) => ({
54
+ schemaFieldName: u.field_name,
55
+ label: u.label,
56
+ type: u.type,
57
+ ...(u.required !== undefined ? { required: u.required } : {}),
58
+ ...(u.options ? { options: u.options } : {}),
59
+ })),
60
+ };
61
+ const data = await getClient().post("/update-schema", body);
62
+ return toolResult(data);
63
+ }
64
+ catch (error) {
65
+ return handleToolError(error);
66
+ }
67
+ });
68
+ // ── delete_columns ─────────────────────────────────────────────────────
69
+ server.tool("delete_columns", "Remove one or more columns (fields) from a lead group's schema and all lead data " +
70
+ "stored in those fields. This action is irreversible — field definitions and values " +
71
+ "across all leads in the group are permanently deleted. Requires admin permissions. " +
72
+ "Always confirm with the user before calling this tool.", {
73
+ lead_group_id: z
74
+ .string()
75
+ .describe("ID of the lead group to remove columns from."),
76
+ column_names: z
77
+ .array(z.string())
78
+ .min(1)
79
+ .describe("Array of column/field names to delete."),
80
+ }, async ({ lead_group_id, column_names }) => {
81
+ try {
82
+ const body = {
83
+ leadGroupId: lead_group_id,
84
+ };
85
+ if (column_names.length === 1) {
86
+ body.columnName = column_names[0];
87
+ }
88
+ else {
89
+ body.columnNames = column_names;
90
+ }
91
+ const data = await getClient().post("/delete-column", body);
92
+ return toolResult(data);
93
+ }
94
+ catch (error) {
95
+ return handleToolError(error);
96
+ }
97
+ });
98
+ // ── update_hidden_columns ──────────────────────────────────────────────
99
+ server.tool("update_hidden_columns", "Show or hide columns in a lead group's table view without deleting underlying data. " +
100
+ "Hidden columns still exist in the schema and retain their values but are not " +
101
+ "displayed in the default UI table. Requires admin permissions. Use this to " +
102
+ "declutter the interface while preserving data — unlike delete_columns, this is reversible.", {
103
+ lead_group_id: z
104
+ .string()
105
+ .describe("ID of the lead group to configure."),
106
+ add_hidden: z
107
+ .array(z.string())
108
+ .optional()
109
+ .describe("Column names to hide from the table view."),
110
+ remove_hidden: z
111
+ .array(z.string())
112
+ .optional()
113
+ .describe("Column names to make visible again in the table view."),
114
+ }, async ({ lead_group_id, add_hidden, remove_hidden }) => {
115
+ try {
116
+ const body = {
117
+ leadGroupId: lead_group_id,
118
+ };
119
+ if (add_hidden)
120
+ body.addHiddenColumns = add_hidden;
121
+ if (remove_hidden)
122
+ body.removeHiddenColumns = remove_hidden;
123
+ const data = await getClient().post("/update-hidden-columns", body);
124
+ return toolResult(data);
125
+ }
126
+ catch (error) {
127
+ return handleToolError(error);
128
+ }
129
+ });
130
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSignalTools(server: McpServer): void;
@@ -0,0 +1,50 @@
1
+ import { z } from "zod";
2
+ import { getClient } from "../client.js";
3
+ import { toolResult, handleToolError } from "../types.js";
4
+ export function registerSignalTools(server) {
5
+ // ── add_signal ─────────────────────────────────────────────────────────
6
+ server.tool("add_signal", "Attach a business intelligence signal to a lead. Signals report key events picked " +
7
+ "up by external agents — budget approvals, position changes, tender publications, " +
8
+ "etc. Each signal has a level (INFO, CRITICAL, GOLDEN) and is shown in the lead's " +
9
+ "feed. Use GOLDEN sparingly for truly actionable buying-intent signals.", {
10
+ lead_id: z.string().describe("ID of the target lead."),
11
+ level: z
12
+ .enum(["INFO", "CRITICAL", "GOLDEN"])
13
+ .describe("Signal priority level."),
14
+ title: z
15
+ .string()
16
+ .describe("Short title of the signal (e.g. 'Budget IRM approuvé')."),
17
+ content: z
18
+ .string()
19
+ .describe("Detailed description of the signal."),
20
+ agent_source: z
21
+ .string()
22
+ .optional()
23
+ .describe("Short identifier of the source agent. MUST be 3 to 4 words max, " +
24
+ "kebab-case or space-separated (e.g. 'leadify qualification', " +
25
+ "'briefify research', 'dealmind coach'). Default: 'unknown'."),
26
+ source_url: z
27
+ .string()
28
+ .url()
29
+ .optional()
30
+ .describe("URL of the original information source."),
31
+ }, async ({ lead_id, level, title, content, agent_source, source_url }) => {
32
+ try {
33
+ const body = {
34
+ leadId: lead_id,
35
+ level,
36
+ title,
37
+ content,
38
+ };
39
+ if (agent_source !== undefined)
40
+ body.agentSource = agent_source;
41
+ if (source_url !== undefined)
42
+ body.sourceUrl = source_url;
43
+ const data = await getClient().post("/add-signal", body);
44
+ return toolResult(data);
45
+ }
46
+ catch (error) {
47
+ return handleToolError(error);
48
+ }
49
+ });
50
+ }
@@ -0,0 +1,16 @@
1
+ export declare class LeadifyApiError extends Error {
2
+ readonly statusCode: number;
3
+ readonly responseBody: unknown;
4
+ constructor(statusCode: number, responseBody: unknown);
5
+ }
6
+ export interface ToolResult {
7
+ [key: string]: unknown;
8
+ content: Array<{
9
+ type: "text";
10
+ text: string;
11
+ }>;
12
+ isError?: boolean;
13
+ }
14
+ export declare function toolResult(data: unknown): ToolResult;
15
+ export declare function toolError(message: string, details?: unknown): ToolResult;
16
+ export declare function handleToolError(error: unknown): ToolResult;
package/dist/types.js ADDED
@@ -0,0 +1,38 @@
1
+ export class LeadifyApiError extends Error {
2
+ statusCode;
3
+ responseBody;
4
+ constructor(statusCode, responseBody) {
5
+ const msg = typeof responseBody === "object" &&
6
+ responseBody !== null &&
7
+ "error" in responseBody
8
+ ? String(responseBody.error)
9
+ : `HTTP ${statusCode}`;
10
+ super(msg);
11
+ this.name = "LeadifyApiError";
12
+ this.statusCode = statusCode;
13
+ this.responseBody = responseBody;
14
+ }
15
+ }
16
+ export function toolResult(data) {
17
+ return {
18
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
19
+ };
20
+ }
21
+ export function toolError(message, details) {
22
+ const error = { error: message };
23
+ if (details !== undefined)
24
+ error.details = details;
25
+ return {
26
+ content: [{ type: "text", text: JSON.stringify(error, null, 2) }],
27
+ isError: true,
28
+ };
29
+ }
30
+ export function handleToolError(error) {
31
+ if (error instanceof LeadifyApiError) {
32
+ return toolError(error.message, {
33
+ statusCode: error.statusCode,
34
+ response: error.responseBody,
35
+ });
36
+ }
37
+ return toolError(error instanceof Error ? error.message : String(error));
38
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@agifyai/leadify-mcp",
3
+ "version": "1.4.1",
4
+ "description": "MCP server for Leadify lead management API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "leadify-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && chmod +x dist/index.js",
16
+ "start": "node dist/index.js",
17
+ "dev": "tsc --watch",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/AgifyAI/mcp_leadify.git"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.12.0",
29
+ "zod": "^3.24.0"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "typescript": "^5.7.0"
34
+ }
35
+ }