conduyt-mcp 4.1.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/.env.example +2 -0
- package/README.md +286 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.js +43 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +79 -0
- package/dist/tools/activities.d.ts +3 -0
- package/dist/tools/activities.js +35 -0
- package/dist/tools/ai-extended.d.ts +3 -0
- package/dist/tools/ai-extended.js +38 -0
- package/dist/tools/ai.d.ts +3 -0
- package/dist/tools/ai.js +22 -0
- package/dist/tools/automations.d.ts +3 -0
- package/dist/tools/automations.js +66 -0
- package/dist/tools/bulk.d.ts +3 -0
- package/dist/tools/bulk.js +71 -0
- package/dist/tools/calendar.d.ts +3 -0
- package/dist/tools/calendar.js +81 -0
- package/dist/tools/companies.d.ts +3 -0
- package/dist/tools/companies.js +67 -0
- package/dist/tools/contacts.d.ts +3 -0
- package/dist/tools/contacts.js +64 -0
- package/dist/tools/custom-fields.d.ts +3 -0
- package/dist/tools/custom-fields.js +48 -0
- package/dist/tools/dashboard.d.ts +3 -0
- package/dist/tools/dashboard.js +7 -0
- package/dist/tools/deals.d.ts +3 -0
- package/dist/tools/deals.js +59 -0
- package/dist/tools/discovery.d.ts +3 -0
- package/dist/tools/discovery.js +28 -0
- package/dist/tools/drip-campaigns.d.ts +3 -0
- package/dist/tools/drip-campaigns.js +98 -0
- package/dist/tools/forms.d.ts +3 -0
- package/dist/tools/forms.js +54 -0
- package/dist/tools/invoices.d.ts +3 -0
- package/dist/tools/invoices.js +76 -0
- package/dist/tools/messaging.d.ts +3 -0
- package/dist/tools/messaging.js +25 -0
- package/dist/tools/notifications.d.ts +3 -0
- package/dist/tools/notifications.js +28 -0
- package/dist/tools/pipelines.d.ts +3 -0
- package/dist/tools/pipelines.js +7 -0
- package/dist/tools/products.d.ts +3 -0
- package/dist/tools/products.js +55 -0
- package/dist/tools/scoring.d.ts +3 -0
- package/dist/tools/scoring.js +48 -0
- package/dist/tools/sequences.d.ts +3 -0
- package/dist/tools/sequences.js +53 -0
- package/dist/tools/smart-lists.d.ts +3 -0
- package/dist/tools/smart-lists.js +54 -0
- package/dist/tools/tags.d.ts +3 -0
- package/dist/tools/tags.js +44 -0
- package/dist/tools/tasks.d.ts +3 -0
- package/dist/tools/tasks.js +40 -0
- package/dist/tools/templates.d.ts +3 -0
- package/dist/tools/templates.js +55 -0
- package/dist/tools/users.d.ts +3 -0
- package/dist/tools/users.js +65 -0
- package/dist/tools/webhooks.d.ts +3 -0
- package/dist/tools/webhooks.js +41 -0
- package/dist/tools/workflow-sandbox.d.ts +3 -0
- package/dist/tools/workflow-sandbox.js +90 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.js +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerBulkTools(server, client) {
|
|
4
|
+
server.tool("conduyt_ai_import_contacts", "AI-powered bulk contact import. Accepts up to 1000 contacts with optional auto-tagging, smart list creation, and automation enrollment. Deduplicates by email, phone, or both.", {
|
|
5
|
+
contacts: z.array(z.object({
|
|
6
|
+
firstName: z.string().optional(),
|
|
7
|
+
lastName: z.string().optional(),
|
|
8
|
+
email: z.string().optional(),
|
|
9
|
+
phone: z.string().optional(),
|
|
10
|
+
company: z.string().optional(),
|
|
11
|
+
jobTitle: z.string().optional(),
|
|
12
|
+
source: z.string().optional(),
|
|
13
|
+
})).describe("Array of contacts to import (1-1000)"),
|
|
14
|
+
options: z.object({
|
|
15
|
+
tags: z.array(z.string()).optional().describe("Tag names to apply to all imported contacts"),
|
|
16
|
+
smartListName: z.string().optional().describe("Create or add to a smart list with this name"),
|
|
17
|
+
enrollInAutomation: z.string().optional().describe("Automation name to enroll imported contacts in"),
|
|
18
|
+
skipDuplicates: z.boolean().optional().describe("Skip duplicate contacts (default true)"),
|
|
19
|
+
duplicateMatchOn: z.enum(["email", "phone", "email_or_phone"]).optional().describe("How to detect duplicates (default: email)"),
|
|
20
|
+
}).optional().describe("Import options — tags, smart list, automation enrollment, dedup strategy"),
|
|
21
|
+
}, async ({ contacts, options }) => {
|
|
22
|
+
const result = await client.post("/api/v1/contacts/ai-import", { contacts, options });
|
|
23
|
+
return formatResult(result);
|
|
24
|
+
});
|
|
25
|
+
server.tool("conduyt_export_contacts", "Export contacts as JSON with filtering. Returns all matching contacts with tags and custom fields.", {
|
|
26
|
+
format: z.enum(["json"]).optional().describe("Export format (use 'json' for MCP)"),
|
|
27
|
+
search: z.string().optional().describe("Search filter"),
|
|
28
|
+
tag: z.string().optional().describe("Filter by tag name"),
|
|
29
|
+
source: z.string().optional().describe("Filter by source"),
|
|
30
|
+
company: z.string().optional().describe("Filter by company"),
|
|
31
|
+
assigned_to: z.string().optional().describe("Filter by assigned user UUID"),
|
|
32
|
+
created_from: z.string().optional().describe("Created after (ISO date)"),
|
|
33
|
+
created_to: z.string().optional().describe("Created before (ISO date)"),
|
|
34
|
+
min_score: z.number().optional().describe("Minimum lead score"),
|
|
35
|
+
}, async (params) => {
|
|
36
|
+
const qp = new URLSearchParams({ format: "json" });
|
|
37
|
+
if (params.search)
|
|
38
|
+
qp.set("search", params.search);
|
|
39
|
+
if (params.tag)
|
|
40
|
+
qp.set("tag", params.tag);
|
|
41
|
+
if (params.source)
|
|
42
|
+
qp.set("source", params.source);
|
|
43
|
+
if (params.company)
|
|
44
|
+
qp.set("company", params.company);
|
|
45
|
+
if (params.assigned_to)
|
|
46
|
+
qp.set("assigned_to", params.assigned_to);
|
|
47
|
+
if (params.created_from)
|
|
48
|
+
qp.set("created_from", params.created_from);
|
|
49
|
+
if (params.created_to)
|
|
50
|
+
qp.set("created_to", params.created_to);
|
|
51
|
+
if (params.min_score)
|
|
52
|
+
qp.set("min_score", String(params.min_score));
|
|
53
|
+
const result = await client.get(`/api/v1/contacts/export?${qp}`);
|
|
54
|
+
return formatResult(result);
|
|
55
|
+
});
|
|
56
|
+
server.tool("conduyt_export_deals", "Export deals as JSON with filtering by status, pipeline, or search term.", {
|
|
57
|
+
search: z.string().optional().describe("Search deal titles"),
|
|
58
|
+
status: z.string().optional().describe("Filter by status (open, won, lost)"),
|
|
59
|
+
pipeline: z.string().optional().describe("Filter by pipeline UUID"),
|
|
60
|
+
}, async (params) => {
|
|
61
|
+
const qp = new URLSearchParams({ format: "json" });
|
|
62
|
+
if (params.search)
|
|
63
|
+
qp.set("search", params.search);
|
|
64
|
+
if (params.status)
|
|
65
|
+
qp.set("status", params.status);
|
|
66
|
+
if (params.pipeline)
|
|
67
|
+
qp.set("pipeline", params.pipeline);
|
|
68
|
+
const result = await client.get(`/api/v1/deals/export?${qp}`);
|
|
69
|
+
return formatResult(result);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerCalendarTools(server, client) {
|
|
4
|
+
server.tool("conduyt_list_calendars", "List all calendars in the account with appointment counts.", {
|
|
5
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
6
|
+
per_page: z.number().optional().describe("Results per page (default 50, max 200)"),
|
|
7
|
+
}, async ({ page, per_page }) => {
|
|
8
|
+
const params = new URLSearchParams();
|
|
9
|
+
if (page)
|
|
10
|
+
params.set("page", String(page));
|
|
11
|
+
if (per_page)
|
|
12
|
+
params.set("per_page", String(per_page));
|
|
13
|
+
const result = await client.get(`/api/v1/calendars?${params}`);
|
|
14
|
+
return formatResult(result);
|
|
15
|
+
});
|
|
16
|
+
server.tool("conduyt_list_appointments", "List appointments with filtering by date range, calendar, contact, assignee, or status.", {
|
|
17
|
+
status: z.string().optional().describe("Filter by status (e.g. 'confirmed', 'cancelled', 'completed')"),
|
|
18
|
+
calendar_id: z.string().optional().describe("Filter by calendar UUID"),
|
|
19
|
+
contact_id: z.string().optional().describe("Filter by contact UUID"),
|
|
20
|
+
assigned_to: z.string().optional().describe("Filter by assigned user UUID"),
|
|
21
|
+
from: z.string().optional().describe("Start of date range (ISO 8601)"),
|
|
22
|
+
to: z.string().optional().describe("End of date range (ISO 8601)"),
|
|
23
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
24
|
+
per_page: z.number().optional().describe("Results per page"),
|
|
25
|
+
}, async ({ status, calendar_id, contact_id, assigned_to, from, to, page, per_page }) => {
|
|
26
|
+
const params = new URLSearchParams();
|
|
27
|
+
if (status)
|
|
28
|
+
params.set("status", status);
|
|
29
|
+
if (calendar_id)
|
|
30
|
+
params.set("calendar_id", calendar_id);
|
|
31
|
+
if (contact_id)
|
|
32
|
+
params.set("contact_id", contact_id);
|
|
33
|
+
if (assigned_to)
|
|
34
|
+
params.set("assigned_to", assigned_to);
|
|
35
|
+
if (from)
|
|
36
|
+
params.set("from", from);
|
|
37
|
+
if (to)
|
|
38
|
+
params.set("to", to);
|
|
39
|
+
if (page)
|
|
40
|
+
params.set("page", String(page));
|
|
41
|
+
if (per_page)
|
|
42
|
+
params.set("per_page", String(per_page));
|
|
43
|
+
const result = await client.get(`/api/v1/appointments?${params}`);
|
|
44
|
+
return formatResult(result);
|
|
45
|
+
});
|
|
46
|
+
server.tool("conduyt_get_appointment", "Get a single appointment with full details — contact, assignee, booking page, and notes.", {
|
|
47
|
+
id: z.string().describe("Appointment UUID"),
|
|
48
|
+
}, async ({ id }) => {
|
|
49
|
+
const result = await client.get(`/api/v1/appointments/${id}`);
|
|
50
|
+
return formatResult(result);
|
|
51
|
+
});
|
|
52
|
+
server.tool("conduyt_create_appointment", "Create a new appointment. Times must include timezone offset (e.g. '2026-05-01T10:00:00-07:00').", {
|
|
53
|
+
title: z.string().describe("Appointment title"),
|
|
54
|
+
startTime: z.string().describe("Start time (ISO 8601 with timezone, e.g. '2026-05-01T10:00:00-07:00')"),
|
|
55
|
+
endTime: z.string().describe("End time (ISO 8601 with timezone)"),
|
|
56
|
+
description: z.string().optional().describe("Appointment description"),
|
|
57
|
+
location: z.string().optional().describe("Location or meeting link"),
|
|
58
|
+
calendarId: z.string().optional().describe("Calendar UUID"),
|
|
59
|
+
contactId: z.string().optional().describe("Associated contact UUID"),
|
|
60
|
+
assignedTo: z.string().optional().describe("Assigned team member UUID"),
|
|
61
|
+
status: z.string().optional().describe("Status (e.g. 'confirmed', 'pending')"),
|
|
62
|
+
notes: z.string().optional().describe("Internal notes"),
|
|
63
|
+
}, async (params) => {
|
|
64
|
+
const result = await client.post("/api/v1/appointments", params);
|
|
65
|
+
return formatResult(result);
|
|
66
|
+
});
|
|
67
|
+
server.tool("conduyt_update_appointment", "Update an appointment — reschedule, reassign, change status, or add notes. Sends cancellation email if status set to 'cancelled'.", {
|
|
68
|
+
id: z.string().describe("Appointment UUID"),
|
|
69
|
+
title: z.string().optional().describe("Updated title"),
|
|
70
|
+
startTime: z.string().optional().describe("Updated start time (ISO 8601 with timezone)"),
|
|
71
|
+
endTime: z.string().optional().describe("Updated end time (ISO 8601 with timezone)"),
|
|
72
|
+
status: z.string().optional().describe("Updated status ('confirmed', 'cancelled', 'completed', etc.)"),
|
|
73
|
+
assignedTo: z.string().optional().describe("Reassign to user UUID"),
|
|
74
|
+
notes: z.string().optional().describe("Updated notes"),
|
|
75
|
+
location: z.string().optional().describe("Updated location"),
|
|
76
|
+
description: z.string().optional().describe("Updated description"),
|
|
77
|
+
}, async ({ id, ...updates }) => {
|
|
78
|
+
const result = await client.patch(`/api/v1/appointments/${id}`, updates);
|
|
79
|
+
return formatResult(result);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerCompanyTools(server, client) {
|
|
4
|
+
server.tool("conduyt_list_companies", "List and search companies. Returns name, domain, industry, contact count, deal count, and total deal value.", {
|
|
5
|
+
search: z.string().optional().describe("Search by company name, domain, or phone"),
|
|
6
|
+
industry: z.string().optional().describe("Filter by industry"),
|
|
7
|
+
size: z.enum(["1-10", "11-50", "51-200", "201-500", "501-1000", "1001+"]).optional().describe("Filter by company size"),
|
|
8
|
+
sort: z.enum(["createdAt", "updatedAt", "name"]).optional().describe("Sort field (default: createdAt)"),
|
|
9
|
+
order: z.enum(["asc", "desc"]).optional().describe("Sort order (default: desc)"),
|
|
10
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
11
|
+
per_page: z.number().optional().describe("Results per page (default 50, max 200)"),
|
|
12
|
+
}, async ({ search, industry, size, sort, order, page, per_page }) => {
|
|
13
|
+
const params = new URLSearchParams();
|
|
14
|
+
if (search)
|
|
15
|
+
params.set("search", search);
|
|
16
|
+
if (industry)
|
|
17
|
+
params.set("industry", industry);
|
|
18
|
+
if (size)
|
|
19
|
+
params.set("size", size);
|
|
20
|
+
if (sort)
|
|
21
|
+
params.set("sort", sort);
|
|
22
|
+
if (order)
|
|
23
|
+
params.set("order", order);
|
|
24
|
+
if (page)
|
|
25
|
+
params.set("page", String(page));
|
|
26
|
+
if (per_page)
|
|
27
|
+
params.set("per_page", String(per_page));
|
|
28
|
+
const result = await client.get(`/api/v1/companies?${params}`);
|
|
29
|
+
return formatResult(result);
|
|
30
|
+
});
|
|
31
|
+
server.tool("conduyt_get_company", "Get a single company with full details — contacts (up to 100), deals (up to 50), and aggregated deal value.", {
|
|
32
|
+
id: z.string().describe("Company UUID"),
|
|
33
|
+
}, async ({ id }) => {
|
|
34
|
+
const result = await client.get(`/api/v1/companies/${id}`);
|
|
35
|
+
return formatResult(result);
|
|
36
|
+
});
|
|
37
|
+
server.tool("conduyt_create_company", "Create a new company record with business details.", {
|
|
38
|
+
name: z.string().describe("Company name (required)"),
|
|
39
|
+
domain: z.string().optional().describe("Company domain (e.g. 'acme.com')"),
|
|
40
|
+
industry: z.string().optional().describe("Industry (e.g. 'Technology', 'Healthcare')"),
|
|
41
|
+
size: z.enum(["1-10", "11-50", "51-200", "201-500", "501-1000", "1001+"]).optional().describe("Company size range"),
|
|
42
|
+
website: z.string().optional().describe("Full website URL"),
|
|
43
|
+
phone: z.string().optional().describe("Company phone number"),
|
|
44
|
+
address: z.string().optional().describe("Street address"),
|
|
45
|
+
city: z.string().optional().describe("City"),
|
|
46
|
+
state: z.string().optional().describe("State/province"),
|
|
47
|
+
zip: z.string().optional().describe("ZIP/postal code"),
|
|
48
|
+
country: z.string().optional().describe("2-letter country code (default: US)"),
|
|
49
|
+
description: z.string().optional().describe("Company description (max 2000 chars)"),
|
|
50
|
+
}, async (params) => {
|
|
51
|
+
const result = await client.post("/api/v1/companies", params);
|
|
52
|
+
return formatResult(result);
|
|
53
|
+
});
|
|
54
|
+
server.tool("conduyt_update_company", "Update a company's details — name, domain, industry, address, or any other field.", {
|
|
55
|
+
id: z.string().describe("Company UUID"),
|
|
56
|
+
name: z.string().optional().describe("Updated company name"),
|
|
57
|
+
domain: z.string().optional().describe("Updated domain"),
|
|
58
|
+
industry: z.string().optional().describe("Updated industry"),
|
|
59
|
+
size: z.enum(["1-10", "11-50", "51-200", "201-500", "501-1000", "1001+"]).optional().describe("Updated size"),
|
|
60
|
+
website: z.string().optional().describe("Updated website URL"),
|
|
61
|
+
phone: z.string().optional().describe("Updated phone"),
|
|
62
|
+
description: z.string().optional().describe("Updated description"),
|
|
63
|
+
}, async ({ id, ...updates }) => {
|
|
64
|
+
const result = await client.patch(`/api/v1/companies/${id}`, updates);
|
|
65
|
+
return formatResult(result);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerContactTools(server, client) {
|
|
4
|
+
server.tool("conduyt_list_contacts", "List and search contacts with filtering. Returns paginated results with name, email, phone, tags, source, and assignee.", {
|
|
5
|
+
search: z.string().optional().describe("Search by name, email, or phone number"),
|
|
6
|
+
tag: z.string().optional().describe("Filter by tag name"),
|
|
7
|
+
source: z.string().optional().describe("Filter by source (e.g. 'signup', 'import', 'manual')"),
|
|
8
|
+
assignedTo: z.string().optional().describe("Filter by assigned user ID"),
|
|
9
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
10
|
+
per_page: z.number().optional().describe("Results per page (default 25, max 100)"),
|
|
11
|
+
}, async ({ search, tag, source, assignedTo, page, per_page }) => {
|
|
12
|
+
const params = new URLSearchParams();
|
|
13
|
+
if (search)
|
|
14
|
+
params.set("search", search);
|
|
15
|
+
if (tag)
|
|
16
|
+
params.set("tag", tag);
|
|
17
|
+
if (source)
|
|
18
|
+
params.set("source", source);
|
|
19
|
+
if (assignedTo)
|
|
20
|
+
params.set("assignedTo", assignedTo);
|
|
21
|
+
if (page)
|
|
22
|
+
params.set("page", String(page));
|
|
23
|
+
if (per_page)
|
|
24
|
+
params.set("per_page", String(per_page));
|
|
25
|
+
const result = await client.get(`/api/v1/contacts?${params}`);
|
|
26
|
+
return formatResult(result);
|
|
27
|
+
});
|
|
28
|
+
server.tool("conduyt_get_contact", "Get a single contact with full details — profile, tags, custom fields, timeline, deals, and activity history.", {
|
|
29
|
+
id: z.string().describe("Contact UUID"),
|
|
30
|
+
}, async ({ id }) => {
|
|
31
|
+
const result = await client.get(`/api/v1/contacts/${id}`);
|
|
32
|
+
return formatResult(result);
|
|
33
|
+
});
|
|
34
|
+
server.tool("conduyt_create_contact", "Create a new contact in the CRM. Email or phone is recommended but not strictly required.", {
|
|
35
|
+
email: z.string().optional().describe("Contact email address"),
|
|
36
|
+
firstName: z.string().optional().describe("First name"),
|
|
37
|
+
lastName: z.string().optional().describe("Last name"),
|
|
38
|
+
phone: z.string().optional().describe("Phone number"),
|
|
39
|
+
company: z.string().optional().describe("Company name"),
|
|
40
|
+
source: z.string().optional().describe("Lead source (e.g. 'website', 'referral', 'import')"),
|
|
41
|
+
tags: z.array(z.string()).optional().describe("Tag names to apply"),
|
|
42
|
+
}, async (params) => {
|
|
43
|
+
const result = await client.post("/api/v1/contacts", params);
|
|
44
|
+
return formatResult(result);
|
|
45
|
+
});
|
|
46
|
+
server.tool("conduyt_update_contact", "Update an existing contact's fields — name, email, phone, company, custom fields.", {
|
|
47
|
+
id: z.string().describe("Contact UUID"),
|
|
48
|
+
email: z.string().optional().describe("Updated email"),
|
|
49
|
+
firstName: z.string().optional().describe("Updated first name"),
|
|
50
|
+
lastName: z.string().optional().describe("Updated last name"),
|
|
51
|
+
phone: z.string().optional().describe("Updated phone"),
|
|
52
|
+
company: z.string().optional().describe("Updated company name"),
|
|
53
|
+
}, async ({ id, ...updates }) => {
|
|
54
|
+
const result = await client.patch(`/api/v1/contacts/${id}`, updates);
|
|
55
|
+
return formatResult(result);
|
|
56
|
+
});
|
|
57
|
+
server.tool("conduyt_add_tags", "Add one or more tags to a contact. Creates the tag if it doesn't exist.", {
|
|
58
|
+
contactId: z.string().describe("Contact UUID"),
|
|
59
|
+
tags: z.array(z.string()).describe("Tag names to add"),
|
|
60
|
+
}, async ({ contactId, tags }) => {
|
|
61
|
+
const result = await client.post(`/api/v1/contacts/${contactId}/tags`, { tags });
|
|
62
|
+
return formatResult(result);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerCustomFieldTools(server, client) {
|
|
4
|
+
server.tool("conduyt_list_custom_fields", "List custom field definitions. Filter by entity type (contact or deal) to see available fields.", {
|
|
5
|
+
entityType: z.enum(["contact", "deal"]).optional().describe("Filter by entity type"),
|
|
6
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
7
|
+
per_page: z.number().optional().describe("Results per page"),
|
|
8
|
+
}, async ({ entityType, page, per_page }) => {
|
|
9
|
+
const params = new URLSearchParams();
|
|
10
|
+
if (entityType)
|
|
11
|
+
params.set("entityType", entityType);
|
|
12
|
+
if (page)
|
|
13
|
+
params.set("page", String(page));
|
|
14
|
+
if (per_page)
|
|
15
|
+
params.set("per_page", String(per_page));
|
|
16
|
+
const result = await client.get(`/api/v1/custom-fields?${params}`);
|
|
17
|
+
return formatResult(result);
|
|
18
|
+
});
|
|
19
|
+
server.tool("conduyt_create_custom_field", "Create a new custom field definition for contacts or deals. Field types: text, number, date, select, multiselect, boolean.", {
|
|
20
|
+
entityType: z.enum(["contact", "deal"]).describe("Entity type this field applies to"),
|
|
21
|
+
fieldKey: z.string().describe("Unique field key (alphanumeric + underscores, e.g. 'lead_score')"),
|
|
22
|
+
label: z.string().describe("Human-readable label (e.g. 'Lead Score')"),
|
|
23
|
+
fieldType: z.enum(["text", "number", "date", "select", "multiselect", "boolean"]).describe("Field data type"),
|
|
24
|
+
section: z.string().optional().describe("Section grouping for UI display"),
|
|
25
|
+
options: z.any().optional().describe("Options for select/multiselect fields (array of strings)"),
|
|
26
|
+
isRequired: z.boolean().optional().describe("Whether this field is required"),
|
|
27
|
+
sortOrder: z.number().optional().describe("Display order"),
|
|
28
|
+
}, async (params) => {
|
|
29
|
+
const result = await client.post("/api/v1/custom-fields", params);
|
|
30
|
+
return formatResult(result);
|
|
31
|
+
});
|
|
32
|
+
server.tool("conduyt_update_custom_field", "Update a custom field's label, options, or required status. Cannot change the field key or entity type.", {
|
|
33
|
+
id: z.string().describe("Custom field UUID"),
|
|
34
|
+
label: z.string().optional().describe("Updated label"),
|
|
35
|
+
options: z.any().optional().describe("Updated options for select/multiselect"),
|
|
36
|
+
isRequired: z.boolean().optional().describe("Updated required status"),
|
|
37
|
+
sortOrder: z.number().optional().describe("Updated display order"),
|
|
38
|
+
}, async ({ id, ...updates }) => {
|
|
39
|
+
const result = await client.patch(`/api/v1/custom-fields/${id}`, updates);
|
|
40
|
+
return formatResult(result);
|
|
41
|
+
});
|
|
42
|
+
server.tool("conduyt_delete_custom_field", "Delete a custom field definition. Removes the field from all entities.", {
|
|
43
|
+
id: z.string().describe("Custom field UUID"),
|
|
44
|
+
}, async ({ id }) => {
|
|
45
|
+
const result = await client.del(`/api/v1/custom-fields/${id}`);
|
|
46
|
+
return formatResult(result);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { formatResult } from "../client.js";
|
|
2
|
+
export function registerDashboardTools(server, client) {
|
|
3
|
+
server.tool("conduyt_dashboard", "Get a full CRM dashboard summary — pipeline stages with deal values, recent activities, contact/deal/task counts, admin metrics (overdue tasks, stale deals, unassigned items, team workload), billing summary, contacts needing follow-up, and overdue tasks.", {}, async () => {
|
|
4
|
+
const result = await client.get("/api/v1/dashboard");
|
|
5
|
+
return formatResult(result);
|
|
6
|
+
});
|
|
7
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerDealTools(server, client) {
|
|
4
|
+
server.tool("conduyt_list_deals", "List deals with filtering by pipeline, stage, assignee, or status. Returns deal title, value, stage, contact, and dates.", {
|
|
5
|
+
pipelineId: z.string().optional().describe("Filter by pipeline UUID"),
|
|
6
|
+
stageId: z.string().optional().describe("Filter by stage UUID"),
|
|
7
|
+
assignedTo: z.string().optional().describe("Filter by assigned user UUID"),
|
|
8
|
+
status: z.enum(["open", "won", "lost"]).optional().describe("Filter by deal status"),
|
|
9
|
+
search: z.string().optional().describe("Search deal titles"),
|
|
10
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
11
|
+
per_page: z.number().optional().describe("Results per page (default 25, max 100)"),
|
|
12
|
+
}, async ({ pipelineId, stageId, assignedTo, status, search, page, per_page }) => {
|
|
13
|
+
const params = new URLSearchParams();
|
|
14
|
+
if (pipelineId)
|
|
15
|
+
params.set("pipelineId", pipelineId);
|
|
16
|
+
if (stageId)
|
|
17
|
+
params.set("stageId", stageId);
|
|
18
|
+
if (assignedTo)
|
|
19
|
+
params.set("assignedTo", assignedTo);
|
|
20
|
+
if (status)
|
|
21
|
+
params.set("status", status);
|
|
22
|
+
if (search)
|
|
23
|
+
params.set("search", search);
|
|
24
|
+
if (page)
|
|
25
|
+
params.set("page", String(page));
|
|
26
|
+
if (per_page)
|
|
27
|
+
params.set("per_page", String(per_page));
|
|
28
|
+
const result = await client.get(`/api/v1/deals?${params}`);
|
|
29
|
+
return formatResult(result);
|
|
30
|
+
});
|
|
31
|
+
server.tool("conduyt_get_deal", "Get a single deal with full details — value, stage, contact, activities, tasks, and history.", {
|
|
32
|
+
id: z.string().describe("Deal UUID"),
|
|
33
|
+
}, async ({ id }) => {
|
|
34
|
+
const result = await client.get(`/api/v1/deals/${id}`);
|
|
35
|
+
return formatResult(result);
|
|
36
|
+
});
|
|
37
|
+
server.tool("conduyt_create_deal", "Create a new deal in a pipeline. Requires a pipeline and stage ID — use conduyt_list_pipelines to discover them.", {
|
|
38
|
+
title: z.string().describe("Deal title (e.g. 'Acme Corp — Enterprise Plan')"),
|
|
39
|
+
pipelineId: z.string().describe("Pipeline UUID"),
|
|
40
|
+
stageId: z.string().describe("Stage UUID within the pipeline"),
|
|
41
|
+
contactId: z.string().optional().describe("Associated contact UUID"),
|
|
42
|
+
value: z.number().optional().describe("Deal value in dollars"),
|
|
43
|
+
assignedTo: z.string().optional().describe("Assigned user UUID"),
|
|
44
|
+
}, async (params) => {
|
|
45
|
+
const result = await client.post("/api/v1/deals", params);
|
|
46
|
+
return formatResult(result);
|
|
47
|
+
});
|
|
48
|
+
server.tool("conduyt_update_deal", "Update a deal — move to a new stage, change value, reassign, or mark as won/lost.", {
|
|
49
|
+
id: z.string().describe("Deal UUID"),
|
|
50
|
+
title: z.string().optional().describe("Updated title"),
|
|
51
|
+
stageId: z.string().optional().describe("Move to a new stage UUID"),
|
|
52
|
+
value: z.number().optional().describe("Updated deal value"),
|
|
53
|
+
status: z.enum(["open", "won", "lost"]).optional().describe("Set deal status"),
|
|
54
|
+
assignedTo: z.string().optional().describe("Reassign to user UUID"),
|
|
55
|
+
}, async ({ id, ...updates }) => {
|
|
56
|
+
const result = await client.patch(`/api/v1/deals/${id}`, updates);
|
|
57
|
+
return formatResult(result);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerDiscoveryTools(server, client) {
|
|
4
|
+
server.tool("conduyt_api_catalog", "Discover all Conduyt CRM API endpoints — 409 endpoints across 32 domains. Call this first to learn what the CRM can do.", {}, async () => {
|
|
5
|
+
const result = await client.get("/api/v1/schema/api-catalog");
|
|
6
|
+
return formatResult(result);
|
|
7
|
+
});
|
|
8
|
+
server.tool("conduyt_check_permissions", "Check what the current API key can do — tier, scopes, rate limits, and usage stats.", {}, async () => {
|
|
9
|
+
const result = await client.get("/api/v1/api-keys/check");
|
|
10
|
+
return formatResult(result);
|
|
11
|
+
});
|
|
12
|
+
server.tool("conduyt_search", "Global search across all CRM entities — contacts, deals, companies, tasks.", {
|
|
13
|
+
query: z.string().describe("Search query — matches names, emails, phone numbers, deal titles"),
|
|
14
|
+
type: z.enum(["contacts", "deals", "companies", "tasks"]).optional().describe("Limit search to a specific entity type"),
|
|
15
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
16
|
+
per_page: z.number().optional().describe("Results per page (default 25, max 100)"),
|
|
17
|
+
}, async ({ query, type, page, per_page }) => {
|
|
18
|
+
const params = new URLSearchParams({ q: query });
|
|
19
|
+
if (type)
|
|
20
|
+
params.set("type", type);
|
|
21
|
+
if (page)
|
|
22
|
+
params.set("page", String(page));
|
|
23
|
+
if (per_page)
|
|
24
|
+
params.set("per_page", String(per_page));
|
|
25
|
+
const result = await client.get(`/api/v1/search?${params}`);
|
|
26
|
+
return formatResult(result);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerDripCampaignTools(server, client) {
|
|
4
|
+
server.tool("conduyt_list_drip_campaigns", "List SMS/email drip campaigns with enrollment counts and provider info.", {
|
|
5
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
6
|
+
per_page: z.number().optional().describe("Results per page (default 25, max 100)"),
|
|
7
|
+
}, async ({ page, per_page }) => {
|
|
8
|
+
const params = new URLSearchParams();
|
|
9
|
+
if (page)
|
|
10
|
+
params.set("page", String(page));
|
|
11
|
+
if (per_page)
|
|
12
|
+
params.set("per_page", String(per_page));
|
|
13
|
+
const result = await client.get(`/api/v1/drip-campaigns?${params}`);
|
|
14
|
+
return formatResult(result);
|
|
15
|
+
});
|
|
16
|
+
server.tool("conduyt_get_drip_campaign", "Get a single drip campaign with full details — steps, delays, send windows, and provider config.", {
|
|
17
|
+
id: z.string().describe("Drip campaign UUID"),
|
|
18
|
+
}, async ({ id }) => {
|
|
19
|
+
const result = await client.get(`/api/v1/drip-campaigns/${id}`);
|
|
20
|
+
return formatResult(result);
|
|
21
|
+
});
|
|
22
|
+
server.tool("conduyt_create_drip_campaign", "Create a new drip campaign with step delays, send windows, and provider configuration.", {
|
|
23
|
+
name: z.string().describe("Campaign name"),
|
|
24
|
+
provider: z.string().describe("Channel — 'sms' or 'email'"),
|
|
25
|
+
urlSegment: z.string().describe("URL segment for tracking"),
|
|
26
|
+
baseUrl: z.string().describe("Base URL for tracking links"),
|
|
27
|
+
tagPrefix: z.string().optional().describe("Prefix for auto-generated step tags"),
|
|
28
|
+
stepDelays: z.array(z.number()).optional().describe("Delay in seconds between each step"),
|
|
29
|
+
totalSteps: z.number().optional().describe("Total number of steps"),
|
|
30
|
+
sendDays: z.array(z.number()).optional().describe("Days to send (0=Sun, 1=Mon, ... 6=Sat)"),
|
|
31
|
+
sendStartHour: z.number().optional().describe("Earliest hour to send (0-23)"),
|
|
32
|
+
sendEndHour: z.number().optional().describe("Latest hour to send (0-23)"),
|
|
33
|
+
timezone: z.string().optional().describe("IANA timezone (e.g. 'America/New_York')"),
|
|
34
|
+
batchSize: z.number().optional().describe("Messages per batch (1-50)"),
|
|
35
|
+
smsProviderId: z.string().optional().describe("SMS provider UUID (for SMS campaigns)"),
|
|
36
|
+
}, async (params) => {
|
|
37
|
+
const result = await client.post("/api/v1/drip-campaigns", params);
|
|
38
|
+
return formatResult(result);
|
|
39
|
+
});
|
|
40
|
+
server.tool("conduyt_update_drip_campaign", "Update a drip campaign — change name, schedule, provider, or activation status.", {
|
|
41
|
+
id: z.string().describe("Drip campaign UUID"),
|
|
42
|
+
name: z.string().optional().describe("Updated name"),
|
|
43
|
+
isActive: z.boolean().optional().describe("Enable or disable"),
|
|
44
|
+
stepDelays: z.array(z.number()).optional().describe("Updated step delays"),
|
|
45
|
+
sendDays: z.array(z.number()).optional().describe("Updated send days"),
|
|
46
|
+
sendStartHour: z.number().optional().describe("Updated start hour"),
|
|
47
|
+
sendEndHour: z.number().optional().describe("Updated end hour"),
|
|
48
|
+
timezone: z.string().optional().describe("Updated timezone"),
|
|
49
|
+
batchSize: z.number().optional().describe("Updated batch size"),
|
|
50
|
+
smsProviderId: z.string().optional().describe("Updated SMS provider"),
|
|
51
|
+
}, async ({ id, ...updates }) => {
|
|
52
|
+
const result = await client.patch(`/api/v1/drip-campaigns/${id}`, updates);
|
|
53
|
+
return formatResult(result);
|
|
54
|
+
});
|
|
55
|
+
server.tool("conduyt_list_drip_enrollments", "List drip campaign enrollments with contact and campaign details. Filter by campaign, contact, or status.", {
|
|
56
|
+
campaignId: z.string().optional().describe("Filter by campaign UUID"),
|
|
57
|
+
contactId: z.string().optional().describe("Filter by contact UUID"),
|
|
58
|
+
status: z.enum(["active", "paused", "stopped"]).optional().describe("Filter by enrollment status"),
|
|
59
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
60
|
+
limit: z.number().optional().describe("Results per page (default 50, max 100)"),
|
|
61
|
+
}, async ({ campaignId, contactId, status, page, limit }) => {
|
|
62
|
+
const params = new URLSearchParams();
|
|
63
|
+
if (campaignId)
|
|
64
|
+
params.set("campaignId", campaignId);
|
|
65
|
+
if (contactId)
|
|
66
|
+
params.set("contactId", contactId);
|
|
67
|
+
if (status)
|
|
68
|
+
params.set("status", status);
|
|
69
|
+
if (page)
|
|
70
|
+
params.set("page", String(page));
|
|
71
|
+
if (limit)
|
|
72
|
+
params.set("limit", String(limit));
|
|
73
|
+
const result = await client.get(`/api/v1/drip-enrollments?${params}`);
|
|
74
|
+
return formatResult(result);
|
|
75
|
+
});
|
|
76
|
+
server.tool("conduyt_batch_pause_enrollments", "Pause all active enrollments for a campaign or smart list.", {
|
|
77
|
+
campaignId: z.string().optional().describe("Pause enrollments in this campaign"),
|
|
78
|
+
smartListId: z.string().optional().describe("Pause enrollments for contacts in this smart list"),
|
|
79
|
+
}, async (params) => {
|
|
80
|
+
const result = await client.post("/api/v1/drip-enrollments/batch/pause", params);
|
|
81
|
+
return formatResult(result);
|
|
82
|
+
});
|
|
83
|
+
server.tool("conduyt_batch_resume_enrollments", "Resume paused enrollments for a campaign or smart list.", {
|
|
84
|
+
campaignId: z.string().optional().describe("Resume enrollments in this campaign"),
|
|
85
|
+
smartListId: z.string().optional().describe("Resume enrollments for contacts in this smart list"),
|
|
86
|
+
}, async (params) => {
|
|
87
|
+
const result = await client.post("/api/v1/drip-enrollments/batch/resume", params);
|
|
88
|
+
return formatResult(result);
|
|
89
|
+
});
|
|
90
|
+
server.tool("conduyt_batch_stop_enrollments", "Permanently stop enrollments for a campaign or smart list. Cannot be undone.", {
|
|
91
|
+
campaignId: z.string().optional().describe("Stop enrollments in this campaign"),
|
|
92
|
+
smartListId: z.string().optional().describe("Stop enrollments for contacts in this smart list"),
|
|
93
|
+
reason: z.string().optional().describe("Reason for stopping (default: 'list_killed')"),
|
|
94
|
+
}, async (params) => {
|
|
95
|
+
const result = await client.post("/api/v1/drip-enrollments/batch/stop", params);
|
|
96
|
+
return formatResult(result);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatResult } from "../client.js";
|
|
3
|
+
export function registerFormTools(server, client) {
|
|
4
|
+
server.tool("conduyt_list_forms", "List web forms with submission counts. Filter by active status or search by name.", {
|
|
5
|
+
search: z.string().optional().describe("Search forms by name"),
|
|
6
|
+
is_active: z.boolean().optional().describe("Filter by active/inactive status"),
|
|
7
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
8
|
+
per_page: z.number().optional().describe("Results per page"),
|
|
9
|
+
}, async ({ search, is_active, page, per_page }) => {
|
|
10
|
+
const params = new URLSearchParams();
|
|
11
|
+
if (search)
|
|
12
|
+
params.set("search", search);
|
|
13
|
+
if (is_active !== undefined)
|
|
14
|
+
params.set("is_active", String(is_active));
|
|
15
|
+
if (page)
|
|
16
|
+
params.set("page", String(page));
|
|
17
|
+
if (per_page)
|
|
18
|
+
params.set("per_page", String(per_page));
|
|
19
|
+
const result = await client.get(`/api/v1/forms?${params}`);
|
|
20
|
+
return formatResult(result);
|
|
21
|
+
});
|
|
22
|
+
server.tool("conduyt_get_form", "Get a single form with fields, settings, and submission count.", {
|
|
23
|
+
id: z.string().describe("Form UUID"),
|
|
24
|
+
}, async ({ id }) => {
|
|
25
|
+
const result = await client.get(`/api/v1/forms/${id}`);
|
|
26
|
+
return formatResult(result);
|
|
27
|
+
});
|
|
28
|
+
server.tool("conduyt_create_form", "Create a new web form. Generates a unique slug automatically if not provided.", {
|
|
29
|
+
name: z.string().describe("Form name"),
|
|
30
|
+
description: z.string().optional().describe("Form description"),
|
|
31
|
+
slug: z.string().optional().describe("URL slug (auto-generated if omitted)"),
|
|
32
|
+
fields: z.any().optional().describe("Form field definitions array"),
|
|
33
|
+
settings: z.any().optional().describe("Form settings object"),
|
|
34
|
+
redirectUrl: z.string().optional().describe("URL to redirect after submission"),
|
|
35
|
+
thankYouMessage: z.string().optional().describe("Message shown after submission"),
|
|
36
|
+
isActive: z.boolean().optional().describe("Whether form accepts submissions (default true)"),
|
|
37
|
+
}, async (params) => {
|
|
38
|
+
const result = await client.post("/api/v1/forms", params);
|
|
39
|
+
return formatResult(result);
|
|
40
|
+
});
|
|
41
|
+
server.tool("conduyt_list_form_submissions", "List submissions for a specific form with contact details.", {
|
|
42
|
+
formId: z.string().describe("Form UUID"),
|
|
43
|
+
page: z.number().optional().describe("Page number (default 1)"),
|
|
44
|
+
per_page: z.number().optional().describe("Results per page"),
|
|
45
|
+
}, async ({ formId, page, per_page }) => {
|
|
46
|
+
const params = new URLSearchParams();
|
|
47
|
+
if (page)
|
|
48
|
+
params.set("page", String(page));
|
|
49
|
+
if (per_page)
|
|
50
|
+
params.set("per_page", String(per_page));
|
|
51
|
+
const result = await client.get(`/api/v1/forms/${formId}/submissions?${params}`);
|
|
52
|
+
return formatResult(result);
|
|
53
|
+
});
|
|
54
|
+
}
|