@agifyai/leadify-mcp 8.4.0 → 8.4.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 +7 -0
- package/dist/server.js +3 -2
- package/dist/tools/auth.d.ts +5 -1
- package/dist/tools/auth.js +31 -2
- package/dist/tools/leads.js +35 -15
- package/dist/version.d.ts +3 -0
- package/dist/version.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Serveur MCP (Model Context Protocol) pour l'API Leadify. Expose les endpoints RE
|
|
|
4
4
|
|
|
5
5
|
Package npm : [`@agifyai/leadify-mcp`](https://www.npmjs.com/package/@agifyai/leadify-mcp)
|
|
6
6
|
|
|
7
|
+
`get_mcp_runtime_info` est le diagnostic de livraison en lecture seule. Il expose la version exacte du package et du serveur, puis vérifie qu'un `organization_id` explicitement choisi est accessible à la clé configurée. Il ne lit aucun prospect et n'effectue aucune écriture, aucun envoi, aucune publication ni activation.
|
|
8
|
+
|
|
7
9
|
---
|
|
8
10
|
|
|
9
11
|
## 📦 Pour les utilisateurs
|
|
@@ -193,6 +195,11 @@ Conséquences pratiques :
|
|
|
193
195
|
| `get_leads` | Rechercher et lister des leads avec filtres, recherche et pagination. |
|
|
194
196
|
| `get_lead` | Récupérer les détails complets d'un lead par son ID. |
|
|
195
197
|
| `update_lead` | Mettre à jour un ou plusieurs champs d'un lead existant. |
|
|
198
|
+
|
|
199
|
+
Pour `add_leads` et `update_lead`, `location` utilise l’objet canonique
|
|
200
|
+
`{ city, region?, postalCode?, countryCode, street? }`. `city` et le code pays
|
|
201
|
+
ISO-2 `countryCode` sont obligatoires. La projection `geo` est calculée par
|
|
202
|
+
Leadify et ne doit jamais être envoyée par un agent MCP.
|
|
196
203
|
| `delete_sequence_messages` | Supprimer uniquement les messages de séquence générés d'un lead, après confirmation explicite. |
|
|
197
204
|
| `delete_leads` | Supprimer définitivement des leads par leurs IDs. |
|
|
198
205
|
| `update_schema` | Ajouter ou modifier les définitions de champs d'un groupe. |
|
package/dist/server.js
CHANGED
|
@@ -15,10 +15,11 @@ import { registerFineTuningTools } from "./tools/fine_tuning.js";
|
|
|
15
15
|
import { registerContextWorkspaceTools } from "./tools/context_workspace.js";
|
|
16
16
|
import { registerLeadViewTools } from "./tools/views.js";
|
|
17
17
|
import { registerRelationshipTools } from "./tools/relationships.js";
|
|
18
|
+
import { MCP_SERVER_NAME, MCP_VERSION } from "./version.js";
|
|
18
19
|
export function createServer() {
|
|
19
20
|
const server = new McpServer({
|
|
20
|
-
name:
|
|
21
|
-
version:
|
|
21
|
+
name: MCP_SERVER_NAME,
|
|
22
|
+
version: MCP_VERSION,
|
|
22
23
|
});
|
|
23
24
|
registerAuthTools(server);
|
|
24
25
|
registerOrganizationTools(server);
|
package/dist/tools/auth.d.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
|
|
2
|
+
import { getClient } from "../client.js";
|
|
3
|
+
type ReadClient = Pick<ReturnType<typeof getClient>, "get">;
|
|
4
|
+
export declare function projectMcpRuntimeInfo(data: unknown, organizationId: string): Record<string, unknown>;
|
|
5
|
+
export declare function registerAuthTools(server: McpServer, injectedClient?: ReadClient): void;
|
|
6
|
+
export {};
|
package/dist/tools/auth.js
CHANGED
|
@@ -1,16 +1,45 @@
|
|
|
1
1
|
import { getClient } from "../client.js";
|
|
2
2
|
import { toolResult, handleToolError } from "../types.js";
|
|
3
|
-
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { MCP_PACKAGE_NAME, MCP_SERVER_NAME, MCP_VERSION } from "../version.js";
|
|
5
|
+
export function projectMcpRuntimeInfo(data, organizationId) {
|
|
6
|
+
const source = data && typeof data === "object" ? data : {};
|
|
7
|
+
const organizations = Array.isArray(source.organizations) ? source.organizations : [];
|
|
8
|
+
const organization = organizations.find((candidate) => candidate.id === organizationId);
|
|
9
|
+
return {
|
|
10
|
+
package: MCP_PACKAGE_NAME,
|
|
11
|
+
server: MCP_SERVER_NAME,
|
|
12
|
+
version: MCP_VERSION,
|
|
13
|
+
organization: organization ? { id: organization.id, name: organization.name ?? null, slug: organization.slug ?? null } : null,
|
|
14
|
+
organizationAccessible: Boolean(organization),
|
|
15
|
+
readOnly: true,
|
|
16
|
+
writes: 0,
|
|
17
|
+
sends: 0,
|
|
18
|
+
externalActivation: 0,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function registerAuthTools(server, injectedClient) {
|
|
4
22
|
server.tool("test_api_key", "Verify that the configured Leadify API key is valid and has not been revoked. " +
|
|
5
23
|
"Returns a success message if the key is active. Use this as a health check " +
|
|
6
24
|
"before performing other operations, or to diagnose authentication errors " +
|
|
7
25
|
"(401/403) from other tools.", {}, async () => {
|
|
8
26
|
try {
|
|
9
|
-
const data = await getClient().get("/test-api-key");
|
|
27
|
+
const data = await (injectedClient ?? getClient()).get("/test-api-key");
|
|
10
28
|
return toolResult(data);
|
|
11
29
|
}
|
|
12
30
|
catch (error) {
|
|
13
31
|
return handleToolError(error);
|
|
14
32
|
}
|
|
15
33
|
});
|
|
34
|
+
server.tool("get_mcp_runtime_info", "Read the exact Leadify MCP package/server version and verify that one explicitly selected organization is accessible to the configured API key. This diagnostic is read-only: it never reads prospects and never writes, sends, schedules, publishes, or activates anything.", {
|
|
35
|
+
organization_id: z.string().min(1).describe("Exact organization ID selected by the caller. The server never chooses an organization implicitly."),
|
|
36
|
+
}, async ({ organization_id }) => {
|
|
37
|
+
try {
|
|
38
|
+
const data = await (injectedClient ?? getClient()).get("/api/organizations");
|
|
39
|
+
return toolResult(projectMcpRuntimeInfo(data, organization_id));
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return handleToolError(error);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
16
45
|
}
|
package/dist/tools/leads.js
CHANGED
|
@@ -2,6 +2,37 @@ import { z } from "zod";
|
|
|
2
2
|
import { getClient } from "../client.js";
|
|
3
3
|
import { toolResult, handleToolError } from "../types.js";
|
|
4
4
|
const DEFAULT_LEAD_FIELDS = ["firstName", "lastName", "email", "company", "jobTitle", "linkedin"];
|
|
5
|
+
const canonicalLocationSchema = z.object({
|
|
6
|
+
city: z.string().trim().min(1).max(200),
|
|
7
|
+
region: z.string().trim().min(1).max(500).optional(),
|
|
8
|
+
postalCode: z.string().trim().min(1).max(500).optional(),
|
|
9
|
+
countryCode: z.string().trim().regex(/^[A-Za-z]{2}$/).transform((value) => value.toUpperCase()),
|
|
10
|
+
street: z.string().trim().min(1).max(500).optional(),
|
|
11
|
+
}).strict().describe("Canonical Leadify location source. city and ISO-2 countryCode are required; region, postalCode and street are optional. geo is system-owned and forbidden.");
|
|
12
|
+
const leadInputSchema = z.record(z.unknown()).superRefine((lead, context) => {
|
|
13
|
+
if (!Object.hasOwn(lead, "location"))
|
|
14
|
+
return;
|
|
15
|
+
const parsed = canonicalLocationSchema.safeParse(lead.location);
|
|
16
|
+
if (!parsed.success) {
|
|
17
|
+
for (const issue of parsed.error.issues) {
|
|
18
|
+
context.addIssue({ ...issue, path: ["location", ...issue.path] });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const leadUpdateSchema = z.object({
|
|
23
|
+
property_name: z.string().describe("Name of the field to update (e.g. 'email', 'status', 'card_analysis')."),
|
|
24
|
+
value: z.unknown().describe("New value for the field. location must use the canonical object; location.geo is forbidden."),
|
|
25
|
+
is_select: z.boolean().optional().describe("If true, the field is created/updated as a 'select' dropdown type. Use for categorical fields like status, source, etc."),
|
|
26
|
+
}).superRefine((update, context) => {
|
|
27
|
+
if (update.property_name !== "location")
|
|
28
|
+
return;
|
|
29
|
+
const parsed = canonicalLocationSchema.safeParse(update.value);
|
|
30
|
+
if (!parsed.success) {
|
|
31
|
+
for (const issue of parsed.error.issues) {
|
|
32
|
+
context.addIssue({ ...issue, path: ["value", ...issue.path] });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
5
36
|
function projectLead(lead, fields) {
|
|
6
37
|
const source = lead && typeof lead === "object" ? lead : {};
|
|
7
38
|
const data = source.data && typeof source.data === "object" ? source.data : {};
|
|
@@ -32,10 +63,10 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
32
63
|
.string()
|
|
33
64
|
.describe("ID of the lead group to add leads to."),
|
|
34
65
|
leads: z
|
|
35
|
-
.array(
|
|
66
|
+
.array(leadInputSchema)
|
|
36
67
|
.describe("Array of lead objects. Each object is a key-value map of field names to values. " +
|
|
37
68
|
"Common fields: email, firstName, lastName, company, jobTitle, phone, linkedin, " +
|
|
38
|
-
"website,
|
|
69
|
+
"website, seniority, specialty, age. location must be {city, region?, postalCode?, countryCode, street?}; geo is computed by Leadify. Also supports card_* fields, " +
|
|
39
70
|
"percent_* fields and boolean flags (decisionMaking, excluded). Canonical relation fields are rejected."),
|
|
40
71
|
is_select_fields: z
|
|
41
72
|
.array(z.string())
|
|
@@ -152,22 +183,11 @@ export function registerLeadTools(server, client = getClient()) {
|
|
|
152
183
|
"Supports all field types including special fields (card_*, percent_*, boolean flags " +
|
|
153
184
|
"like decisionMaking/excluded). When a configured canonical relation field is changed, " +
|
|
154
185
|
"the backend routes it through the typed relationship service and keeps the JSON field read-only. Returns the updated lead. " +
|
|
186
|
+
"location must be the canonical object {city, region?, postalCode?, countryCode, street?}; never submit geo. " +
|
|
155
187
|
"For bulk updates across many leads, call this tool once per lead.", {
|
|
156
188
|
lead_id: z.string().describe("ID of the lead to update."),
|
|
157
189
|
updates: z
|
|
158
|
-
.array(
|
|
159
|
-
property_name: z
|
|
160
|
-
.string()
|
|
161
|
-
.describe("Name of the field to update (e.g. 'email', 'status', 'card_analysis')."),
|
|
162
|
-
value: z
|
|
163
|
-
.unknown()
|
|
164
|
-
.describe("New value for the field. Type depends on the field."),
|
|
165
|
-
is_select: z
|
|
166
|
-
.boolean()
|
|
167
|
-
.optional()
|
|
168
|
-
.describe("If true, the field is created/updated as a 'select' dropdown type. " +
|
|
169
|
-
"Use for categorical fields like status, source, etc."),
|
|
170
|
-
}))
|
|
190
|
+
.array(leadUpdateSchema)
|
|
171
191
|
.min(1)
|
|
172
192
|
.describe("Array of field updates to apply."),
|
|
173
193
|
}, async ({ lead_id, updates }) => {
|
package/dist/version.js
ADDED