conduyt-mcp 4.33.0 → 4.35.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/index.js CHANGED
@@ -41,6 +41,7 @@ import { registerLifecycleTools } from "./tools/lifecycle.js";
41
41
  import { registerReplyCaptureTools } from "./tools/reply-capture.js";
42
42
  import { registerCallFlowTools } from "./tools/call-flows.js";
43
43
  import { registerProjectBlueTools } from "./tools/project-blue.js";
44
+ import { registerReportTools } from "./tools/reports.js";
44
45
  const apiUrl = process.env.CONDUYT_API_URL;
45
46
  const apiKey = process.env.CONDUYT_API_KEY;
46
47
  if (!apiUrl || !apiKey) {
@@ -104,5 +105,6 @@ registerLifecycleTools(server, client);
104
105
  registerReplyCaptureTools(server, client);
105
106
  registerCallFlowTools(server, client);
106
107
  registerProjectBlueTools(server, client);
108
+ registerReportTools(server, client);
107
109
  const transport = new StdioServerTransport();
108
110
  await server.connect(transport);
@@ -21,9 +21,11 @@ export function registerContactTools(server, client) {
21
21
  tag: z.string().optional().describe("Filter by tag name"),
22
22
  source: z.string().optional().describe("Filter by source (e.g. 'signup', 'import', 'manual')"),
23
23
  assignedTo: z.string().optional().describe("Filter by assigned user ID"),
24
+ masterStatus: z.array(z.string().trim().min(1).max(40)).max(20).optional().describe("Only contacts whose LEAD (master) status is one of these: 'open', 'won', 'lost', 'abandoned', 'disqualified' or an account custom status. Unknown values are a 400."),
25
+ excludeMasterStatus: z.array(z.string().trim().min(1).max(40)).max(20).optional().describe("Hide contacts whose lead status is one of these — e.g. ['won','lost','abandoned','disqualified'] is the 'Active' view. Naming a status in both lists is a 400."),
24
26
  page: z.number().optional().describe("Page number (default 1)"),
25
27
  per_page: z.number().optional().describe("Results per page (default 25, max 100)"),
26
- }, async ({ search, tag, source, assignedTo, page, per_page }) => {
28
+ }, async ({ search, tag, source, assignedTo, masterStatus, excludeMasterStatus, page, per_page }) => {
27
29
  const params = new URLSearchParams();
28
30
  if (search)
29
31
  params.set("search", search);
@@ -33,6 +35,10 @@ export function registerContactTools(server, client) {
33
35
  params.set("source", source);
34
36
  if (assignedTo)
35
37
  params.set("assigned_to", assignedTo);
38
+ if (masterStatus?.length)
39
+ params.set("master_status", masterStatus.join(","));
40
+ if (excludeMasterStatus?.length)
41
+ params.set("exclude_master_status", excludeMasterStatus.join(","));
36
42
  if (page)
37
43
  params.set("page", String(page));
38
44
  if (per_page)
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ConduytClient } from "../client.js";
3
+ export declare function registerReportTools(server: McpServer, client: ConduytClient): void;
@@ -0,0 +1,105 @@
1
+ import { z } from "zod";
2
+ import { formatResult } from "../client.js";
3
+ /**
4
+ * Custom reports (reporting P1, 2026-09-10): the Lead Activity grid. A report is a saved definition — a base
5
+ * (contacts, deals, activities, tasks, messages, invoices), columns, filters, a sort, an optional metric and
6
+ * grouping. On the contacts and deals bases the columns can be per-row ROLLUPS (calls, connected calls, talk time,
7
+ * first/last call, SMS and email counts, time to first touch, activities, deals and their value, appointments, drip
8
+ * status, days since created or contacted; on deals: contact name/phone/email, age, time in stage, days to close),
9
+ * the account's CUSTOM FIELDS (`cf:<key>`), a Smart View as the POPULATION, and a second grouping dimension.
10
+ * Reads need the reports scope; creating needs reports create; exporting needs reports export.
11
+ */
12
+ const filterSchema = z.object({
13
+ field: z.string().describe("A field name from conduyt_report_fields (base column, rollup, or cf:<key>)"),
14
+ operator: z.string().describe("eq, neq, contains, gt, gte, lt, lte, between, in, isNull, isNotNull, or a relative-date operator (within_last_n, today, since_monday, this_calendar_month, previous_month, in_past, in_future…)"),
15
+ value: z.unknown().optional().describe("The value; [from, to] for between; a list for in; {n, unit} for within_last_n / within_next_n"),
16
+ });
17
+ export function registerReportTools(server, client) {
18
+ server.tool("conduyt_report_fields", "The field catalogue a custom report may use on a base: the entity's own columns, its per-row rollups (contacts/deals) and the account's custom fields, with type, operators and whether each can group, sort or be measured. Read this before building a report.", { entity: z.enum(["contacts", "deals", "activities", "tasks", "messages", "invoices"]).describe("The report base") }, async ({ entity }) => formatResult(await client.get(`/api/v1/reports/custom/fields?entity=${encodeURIComponent(entity)}`)));
19
+ server.tool("conduyt_list_reports", "List the account's saved custom reports (shared ones and your own), with their definition and last-run status.", {}, async () => formatResult(await client.get("/api/v1/reports/custom")));
20
+ server.tool("conduyt_create_report", "Create a saved custom report. contacts/deals bases support rollup columns, cf:<key> custom fields, a Smart View population and groupBy2 (see conduyt_report_fields).", {
21
+ name: z.string().min(1).max(120),
22
+ description: z.string().max(500).optional(),
23
+ entity: z.enum(["contacts", "deals", "activities", "tasks", "messages", "invoices"]),
24
+ columns: z.array(z.string()).min(1).max(60).describe("Field names in display order"),
25
+ filters: z.array(filterSchema).max(30).optional(),
26
+ sortBy: z.string().optional(),
27
+ sortDir: z.enum(["asc", "desc"]).optional(),
28
+ groupBy: z.string().optional().describe("A groupable field (chart dimension)"),
29
+ groupBy2: z.string().optional().describe("contacts/deals only: a second grouping dimension (rows = groupBy × groupBy2)"),
30
+ timeGrain: z.enum(["day", "week", "month"]).optional().describe("When groupBy is a date"),
31
+ aggregate: z.enum(["count", "sum", "avg", "min", "max"]).optional(),
32
+ measureField: z.string().optional().describe("A numeric field for sum/avg/min/max (a rollup or cf:<key> works)"),
33
+ chartType: z.enum(["bar", "line", "pie", "table", "funnel", "number"]).optional(),
34
+ population: z.object({ smartViewId: z.string().uuid().nullable().optional() }).optional().describe("contacts/deals only: { smartViewId } runs the report over the contacts in that Smart View"),
35
+ isShared: z.boolean().optional().describe("Visible to the whole account (default false = only you and admins)"),
36
+ }, async ({ name, description, entity, columns, filters, sortBy, sortDir, groupBy, groupBy2, timeGrain, aggregate, measureField, chartType, population, isShared }) => {
37
+ // Inline body (undefined fields drop out in JSON) — readable by the contract verifier's static parser.
38
+ const result = await client.post("/api/v1/reports/custom", {
39
+ name,
40
+ description,
41
+ entity,
42
+ columns,
43
+ filters,
44
+ sortBy,
45
+ sortDir,
46
+ groupBy,
47
+ groupBy2,
48
+ timeGrain,
49
+ aggregate,
50
+ measureField,
51
+ chartType,
52
+ population,
53
+ isShared,
54
+ });
55
+ return formatResult(result);
56
+ });
57
+ server.tool("conduyt_run_report", "Run a saved custom report: a page of rows (per_page ≤ 500), the total, the metric, the chart data (groupBy) and, on contacts/deals with groupBy2, the two-dimensional grouped rows.", {
58
+ id: z.string().uuid().describe("Saved report id (from conduyt_list_reports)"),
59
+ page: z.number().int().min(1).optional(),
60
+ per_page: z.number().int().min(1).max(500).optional(),
61
+ }, async ({ id, page, per_page }) => {
62
+ const params = new URLSearchParams();
63
+ if (page)
64
+ params.set("page", String(page));
65
+ if (per_page)
66
+ params.set("per_page", String(per_page));
67
+ const qs = params.toString();
68
+ return formatResult(await client.post(`/api/v1/reports/custom/${encodeURIComponent(id)}/run${qs ? `?${qs}` : ""}`, {}));
69
+ });
70
+ server.tool("conduyt_preview_report", "Run an UNSAVED report definition (10 rows) to check columns and filters before saving. Same fields as conduyt_create_report.", {
71
+ entity: z.enum(["contacts", "deals", "activities", "tasks", "messages", "invoices"]),
72
+ columns: z.array(z.string()).min(1).max(60),
73
+ filters: z.array(filterSchema).max(30).optional(),
74
+ sortBy: z.string().optional(),
75
+ sortDir: z.enum(["asc", "desc"]).optional(),
76
+ groupBy: z.string().optional(),
77
+ groupBy2: z.string().optional(),
78
+ timeGrain: z.enum(["day", "week", "month"]).optional(),
79
+ aggregate: z.enum(["count", "sum", "avg", "min", "max"]).optional(),
80
+ measureField: z.string().optional(),
81
+ population: z.object({ smartViewId: z.string().uuid().nullable().optional() }).optional(),
82
+ }, async ({ entity, columns, filters, sortBy, sortDir, groupBy, groupBy2, timeGrain, aggregate, measureField, population }) => {
83
+ const result = await client.post("/api/v1/reports/custom/preview", {
84
+ entity,
85
+ columns,
86
+ filters,
87
+ sortBy,
88
+ sortDir,
89
+ groupBy,
90
+ groupBy2,
91
+ timeGrain,
92
+ aggregate,
93
+ measureField,
94
+ population,
95
+ });
96
+ return formatResult(result);
97
+ });
98
+ server.tool("conduyt_export_report", "Export every matching row of a saved report (up to 50,000) as CSV or XLSX text. Returns the file body; for a large report narrow the filters or ask for the scheduled delivery.", {
99
+ id: z.string().uuid(),
100
+ format: z.enum(["csv", "xlsx"]).optional().describe("csv (default) or xlsx"),
101
+ }, async ({ id, format }) => {
102
+ const result = await client.postText(`/api/v1/reports/custom/${encodeURIComponent(id)}/export?format=${format ?? "csv"}`, {});
103
+ return formatResult(result);
104
+ });
105
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.33.0",
3
+ "version": "4.35.0",
4
4
  "description": "MCP server for Conduyt CRM — expose CRM operations as AI-accessible tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,11 +1,11 @@
1
1
  {
2
- "generatedAt": "2026-09-09T04:17:05.786Z",
3
- "toolCount": 219,
4
- "totalEndpoints": 929,
2
+ "generatedAt": "2026-09-10T13:43:27.157Z",
3
+ "toolCount": 225,
4
+ "totalEndpoints": 931,
5
5
  "counts": {
6
- "covered": 202,
7
- "excluded": 282,
8
- "planned": 445,
6
+ "covered": 207,
7
+ "excluded": 283,
8
+ "planned": 441,
9
9
  "uncategorized": 0
10
10
  },
11
11
  "matrix": {
@@ -1573,6 +1573,10 @@
1573
1573
  "status": "excluded",
1574
1574
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1575
1575
  },
1576
+ "GET /api/v1/cron/process-auto-dispositions": {
1577
+ "status": "excluded",
1578
+ "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1579
+ },
1576
1580
  "GET /api/v1/cron/process-automation-redispatch": {
1577
1581
  "status": "excluded",
1578
1582
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
@@ -3166,12 +3170,16 @@
3166
3170
  "reason": "additional report surfaces beyond ai/insights"
3167
3171
  },
3168
3172
  "GET /api/v1/reports/custom": {
3169
- "status": "planned",
3170
- "reason": "additional report surfaces beyond ai/insights"
3173
+ "status": "covered",
3174
+ "tools": [
3175
+ "conduyt_list_reports"
3176
+ ]
3171
3177
  },
3172
3178
  "POST /api/v1/reports/custom": {
3173
- "status": "planned",
3174
- "reason": "additional report surfaces beyond ai/insights"
3179
+ "status": "covered",
3180
+ "tools": [
3181
+ "conduyt_create_report"
3182
+ ]
3175
3183
  },
3176
3184
  "GET /api/v1/reports/custom/:id": {
3177
3185
  "status": "planned",
@@ -3186,16 +3194,26 @@
3186
3194
  "reason": "additional report surfaces beyond ai/insights"
3187
3195
  },
3188
3196
  "POST /api/v1/reports/custom/:id/export": {
3189
- "status": "planned",
3190
- "reason": "additional report surfaces beyond ai/insights"
3197
+ "status": "covered",
3198
+ "tools": [
3199
+ "conduyt_export_report"
3200
+ ]
3191
3201
  },
3192
3202
  "POST /api/v1/reports/custom/:id/run": {
3193
3203
  "status": "planned",
3194
3204
  "reason": "additional report surfaces beyond ai/insights"
3195
3205
  },
3206
+ "GET /api/v1/reports/custom/fields": {
3207
+ "status": "covered",
3208
+ "tools": [
3209
+ "conduyt_report_fields"
3210
+ ]
3211
+ },
3196
3212
  "POST /api/v1/reports/custom/preview": {
3197
- "status": "planned",
3198
- "reason": "additional report surfaces beyond ai/insights"
3213
+ "status": "covered",
3214
+ "tools": [
3215
+ "conduyt_preview_report"
3216
+ ]
3199
3217
  },
3200
3218
  "GET /api/v1/reports/dialer/agent-hourly": {
3201
3219
  "status": "planned",