conduyt-mcp 4.3.3 → 4.5.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.
- package/dist/tools/contacts.js +36 -5
- package/dist/tools/deals.js +11 -0
- package/package.json +2 -2
package/dist/tools/contacts.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatResult } from "../client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Accept BOTH the canonical `customFields` and the REST-style `custom_fields`
|
|
5
|
+
* spelling and return the canonical value to send to the API. The MCP SDK
|
|
6
|
+
* strips schema-undeclared keys, so a caller using the snake_case alias (which
|
|
7
|
+
* is plausible — this file also exposes per_page / assigned_to) would otherwise
|
|
8
|
+
* have their custom-field values silently dropped. Declaring both and
|
|
9
|
+
* normalizing here closes that data-loss path; supplying both at once is a
|
|
10
|
+
* conflict we reject rather than silently pick one.
|
|
11
|
+
*/
|
|
12
|
+
function resolveCustomFields(camel, snake) {
|
|
13
|
+
if (camel !== undefined && snake !== undefined) {
|
|
14
|
+
throw new Error("Provide custom-field values once: use either `customFields` or `custom_fields`, not both.");
|
|
15
|
+
}
|
|
16
|
+
return camel !== undefined ? camel : snake;
|
|
17
|
+
}
|
|
3
18
|
export function registerContactTools(server, client) {
|
|
4
19
|
server.tool("conduyt_list_contacts", "List and search contacts with filtering. Returns paginated results with name, email, phone, tags, source, and assignee.", {
|
|
5
20
|
search: z.string().optional().describe("Search by name, email, or phone number"),
|
|
@@ -39,19 +54,27 @@ export function registerContactTools(server, client) {
|
|
|
39
54
|
company: z.string().optional().describe("Company name"),
|
|
40
55
|
source: z.string().optional().describe("Lead source (e.g. 'website', 'referral', 'import')"),
|
|
41
56
|
tags: z.array(z.string()).optional().describe("Tag UUIDs to apply (tags must already exist — create them with conduyt_create_tag first)"),
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
customFields: z.record(z.any()).optional().describe("Structured custom-field values keyed by field key, e.g. { lead_temperature: 'hot', budget: 50000 }. Define fields first with conduyt_create_custom_field (entity 'contact')."),
|
|
58
|
+
custom_fields: z.record(z.any()).optional().describe("Alias for customFields (snake_case). Provide only one of customFields / custom_fields."),
|
|
59
|
+
}, async ({ customFields, custom_fields, ...rest }) => {
|
|
60
|
+
const cf = resolveCustomFields(customFields, custom_fields);
|
|
61
|
+
const body = cf !== undefined ? { ...rest, customFields: cf } : rest;
|
|
62
|
+
const result = await client.post("/api/v1/contacts", body);
|
|
44
63
|
return formatResult(result);
|
|
45
64
|
});
|
|
46
|
-
server.tool("conduyt_update_contact", "Update an existing contact's fields — name, email, phone, company, custom fields.", {
|
|
65
|
+
server.tool("conduyt_update_contact", "Update an existing contact's fields — name, email, phone, company, custom fields. customFields merge into existing values (omit a key to leave it unchanged).", {
|
|
47
66
|
id: z.string().describe("Contact UUID"),
|
|
48
67
|
email: z.string().optional().describe("Updated email"),
|
|
49
68
|
firstName: z.string().optional().describe("Updated first name"),
|
|
50
69
|
lastName: z.string().optional().describe("Updated last name"),
|
|
51
70
|
phone: z.string().optional().describe("Updated phone"),
|
|
52
71
|
company: z.string().optional().describe("Updated company name"),
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
customFields: z.record(z.any()).optional().describe("Structured custom-field values to merge, keyed by field key. Omit a key to leave it unchanged."),
|
|
73
|
+
custom_fields: z.record(z.any()).optional().describe("Alias for customFields (snake_case). Provide only one of customFields / custom_fields."),
|
|
74
|
+
}, async ({ id, customFields, custom_fields, ...updates }) => {
|
|
75
|
+
const cf = resolveCustomFields(customFields, custom_fields);
|
|
76
|
+
const body = cf !== undefined ? { ...updates, customFields: cf } : updates;
|
|
77
|
+
const result = await client.patch(`/api/v1/contacts/${id}`, body);
|
|
55
78
|
return formatResult(result);
|
|
56
79
|
});
|
|
57
80
|
server.tool("conduyt_add_tags", "Add one or more existing tags to a contact by tag UUID. Tags must already exist (create them with conduyt_create_tag); this does NOT create tags.", {
|
|
@@ -61,4 +84,12 @@ export function registerContactTools(server, client) {
|
|
|
61
84
|
const result = await client.post(`/api/v1/contacts/${contactId}/tags`, { tagIds });
|
|
62
85
|
return formatResult(result);
|
|
63
86
|
});
|
|
87
|
+
server.tool("conduyt_link_contact_to_deal", "Associate an existing contact with an existing deal (creates a deal↔contact link). A deal's primary contact is set at create time via contactId; use this to attach ADDITIONAL contacts (e.g. a decision-maker plus an influencer) to the same deal. Both must already exist and belong to your account.", {
|
|
88
|
+
dealId: z.string().describe("Deal UUID"),
|
|
89
|
+
contactId: z.string().describe("Contact UUID to link to the deal"),
|
|
90
|
+
role: z.string().optional().describe("Relationship role for this contact on the deal, e.g. 'decision_maker', 'influencer', 'billing'. Defaults to 'related'."),
|
|
91
|
+
}, async ({ dealId, contactId, role }) => {
|
|
92
|
+
const result = await client.post(`/api/v1/deals/${dealId}/contacts`, { contactId, role });
|
|
93
|
+
return formatResult(result);
|
|
94
|
+
});
|
|
64
95
|
}
|
package/dist/tools/deals.js
CHANGED
|
@@ -141,4 +141,15 @@ export function registerDealTools(server, client) {
|
|
|
141
141
|
const result = await client.post(`/api/v1/imports/${jobId}/process`, {});
|
|
142
142
|
return formatResult({ jobId, ...result });
|
|
143
143
|
});
|
|
144
|
+
server.tool("conduyt_add_deal_tags", "Tag a deal with one or more EXISTING tags by tag UUID. Tags must already exist (create them with conduyt_create_tag); this does NOT create tags. Deals support tags just like contacts — use this to segment high-value or priority deals. To tag several deals, call this once per deal.", {
|
|
145
|
+
dealId: z.string().describe("Deal UUID"),
|
|
146
|
+
tagIds: z.array(z.string()).describe("Tag UUIDs to add to the deal (must already exist; non-empty)"),
|
|
147
|
+
}, async ({ dealId, tagIds }) => {
|
|
148
|
+
const result = await client.post(`/api/v1/deals/${dealId}/tags`, { tagIds });
|
|
149
|
+
return formatResult(result);
|
|
150
|
+
});
|
|
151
|
+
server.tool("conduyt_list_deal_views", "List the account's SAVED DEAL VIEWS (saved deal segments, e.g. 'Open deals over $10k'), each with its id, name, stored filter rules, and current deal count. Read-only — shows how the account segments its deals.", {}, async () => {
|
|
152
|
+
const result = await client.get("/api/v1/deals/views");
|
|
153
|
+
return formatResult(result);
|
|
154
|
+
});
|
|
144
155
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conduyt-mcp",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "MCP server for Conduyt CRM
|
|
3
|
+
"version": "4.5.0",
|
|
4
|
+
"description": "MCP server for Conduyt CRM \u2014 expose CRM operations as AI-accessible tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|