conduyt-mcp 4.34.0 → 4.36.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 +2 -0
- package/dist/tools/reports.d.ts +3 -0
- package/dist/tools/reports.js +140 -0
- package/package.json +1 -1
- package/scripts/coverage-matrix.json +73 -15
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);
|
|
@@ -0,0 +1,140 @@
|
|
|
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
|
+
// ── Reporting P2 (2026-09-10): scheduled delivery of saved reports ────────────────────────────────────────────────
|
|
106
|
+
/** the API takes recipients as objects; the tool takes plain addresses (the contract audit reads the body literally, so the shape lives here) */
|
|
107
|
+
const toRecipients = (emails) => emails.map((email) => ({ email }));
|
|
108
|
+
const scheduleFields = {
|
|
109
|
+
name: z.string().min(1).max(120).optional().describe("A label for the schedule (defaults to the report name)"),
|
|
110
|
+
cadence: z.enum(["daily", "weekly", "monthly"]),
|
|
111
|
+
hourLocal: z.number().int().min(0).max(23).optional().describe("Hour in the workspace timezone (default 7)"),
|
|
112
|
+
minuteLocal: z.number().int().min(0).max(59).optional(),
|
|
113
|
+
weekday: z.number().int().min(0).max(6).optional().describe("weekly: 0 = Sunday … 6 = Saturday"),
|
|
114
|
+
monthday: z.number().int().min(1).max(28).optional().describe("monthly: 1-28"),
|
|
115
|
+
timezone: z.string().optional().describe("IANA zone; defaults to the workspace timezone"),
|
|
116
|
+
formats: z.array(z.enum(["csv", "xlsx", "pdf"])).min(1).describe("The files to build and send"),
|
|
117
|
+
recipients: z.array(z.string().email()).min(1).max(20).describe("Email addresses inside or outside the workspace"),
|
|
118
|
+
rangeMode: z.enum(["as_saved", "rolling"]).optional().describe("rolling = only records created in the period since the last run"),
|
|
119
|
+
enabled: z.boolean().optional(),
|
|
120
|
+
};
|
|
121
|
+
server.tool("conduyt_list_report_schedules", "The scheduled deliveries on a saved report (daily / weekly / monthly emails of the report as CSV, XLSX or PDF): cadence, time, formats, recipients, next and last run.", { id: z.string().uuid().describe("Saved report id") }, async ({ id }) => formatResult(await client.get(`/api/v1/reports/custom/${encodeURIComponent(id)}/subscriptions`)));
|
|
122
|
+
server.tool("conduyt_create_report_schedule", "Schedule a saved report to be emailed on a cadence. The delivery runs with the visibility of whoever creates it. Needs reports export.", { id: z.string().uuid().describe("Saved report id"), ...scheduleFields }, async ({ id, recipients, ...rest }) => formatResult(await client.post(`/api/v1/reports/custom/${encodeURIComponent(id)}/subscriptions`, { ...rest, recipients: toRecipients(recipients) })));
|
|
123
|
+
server.tool("conduyt_update_report_schedule", "Change a schedule (only the fields you pass), including pausing (enabled: false) or resuming it.", {
|
|
124
|
+
sid: z.string().uuid().describe("Schedule id (from conduyt_list_report_schedules)"),
|
|
125
|
+
name: scheduleFields.name,
|
|
126
|
+
cadence: scheduleFields.cadence.optional(),
|
|
127
|
+
hourLocal: scheduleFields.hourLocal,
|
|
128
|
+
minuteLocal: scheduleFields.minuteLocal,
|
|
129
|
+
weekday: scheduleFields.weekday,
|
|
130
|
+
monthday: scheduleFields.monthday,
|
|
131
|
+
timezone: scheduleFields.timezone,
|
|
132
|
+
formats: scheduleFields.formats.optional(),
|
|
133
|
+
recipients: scheduleFields.recipients.optional(),
|
|
134
|
+
rangeMode: scheduleFields.rangeMode,
|
|
135
|
+
enabled: scheduleFields.enabled,
|
|
136
|
+
}, async ({ sid, recipients, ...rest }) => formatResult(await client.patch(`/api/v1/reports/subscriptions/${encodeURIComponent(sid)}`, { ...rest, ...(recipients ? { recipients: toRecipients(recipients) } : {}) })));
|
|
137
|
+
server.tool("conduyt_delete_report_schedule", "Remove a schedule. Past deliveries stay downloadable until they expire (14 days).", { sid: z.string().uuid() }, async ({ sid }) => formatResult(await client.del(`/api/v1/reports/subscriptions/${encodeURIComponent(sid)}`)));
|
|
138
|
+
server.tool("conduyt_send_report_schedule_now", "Build and email a schedule right now (one manual delivery; the schedule itself is untouched). Rate-limited to 3 per 10 minutes per schedule.", { sid: z.string().uuid() }, async ({ sid }) => formatResult(await client.post(`/api/v1/reports/subscriptions/${encodeURIComponent(sid)}/run`, {})));
|
|
139
|
+
server.tool("conduyt_report_schedule_deliveries", "The delivery history of a schedule (newest first): status, rows, whether it was emailed, and download URLs for the files.", { sid: z.string().uuid() }, async ({ sid }) => formatResult(await client.get(`/api/v1/reports/subscriptions/${encodeURIComponent(sid)}/deliveries`)));
|
|
140
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-09-
|
|
3
|
-
"toolCount":
|
|
4
|
-
"totalEndpoints":
|
|
2
|
+
"generatedAt": "2026-09-10T18:42:14.384Z",
|
|
3
|
+
"toolCount": 231,
|
|
4
|
+
"totalEndpoints": 939,
|
|
5
5
|
"counts": {
|
|
6
|
-
"covered":
|
|
6
|
+
"covered": 213,
|
|
7
7
|
"excluded": 283,
|
|
8
|
-
"planned":
|
|
8
|
+
"planned": 443,
|
|
9
9
|
"uncategorized": 0
|
|
10
10
|
},
|
|
11
11
|
"matrix": {
|
|
@@ -1537,6 +1537,10 @@
|
|
|
1537
1537
|
"status": "excluded",
|
|
1538
1538
|
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
1539
1539
|
},
|
|
1540
|
+
"GET /api/v1/cron/deliver-report-subscriptions": {
|
|
1541
|
+
"status": "excluded",
|
|
1542
|
+
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
1543
|
+
},
|
|
1540
1544
|
"GET /api/v1/cron/file-storage-cleanup": {
|
|
1541
1545
|
"status": "excluded",
|
|
1542
1546
|
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
@@ -1789,10 +1793,6 @@
|
|
|
1789
1793
|
"status": "excluded",
|
|
1790
1794
|
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
1791
1795
|
},
|
|
1792
|
-
"POST /api/v1/cron/retry-webhooks": {
|
|
1793
|
-
"status": "excluded",
|
|
1794
|
-
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
1795
|
-
},
|
|
1796
1796
|
"GET /api/v1/cron/scheduled-account-exports": {
|
|
1797
1797
|
"status": "excluded",
|
|
1798
1798
|
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
@@ -3170,12 +3170,16 @@
|
|
|
3170
3170
|
"reason": "additional report surfaces beyond ai/insights"
|
|
3171
3171
|
},
|
|
3172
3172
|
"GET /api/v1/reports/custom": {
|
|
3173
|
-
"status": "
|
|
3174
|
-
"
|
|
3173
|
+
"status": "covered",
|
|
3174
|
+
"tools": [
|
|
3175
|
+
"conduyt_list_reports"
|
|
3176
|
+
]
|
|
3175
3177
|
},
|
|
3176
3178
|
"POST /api/v1/reports/custom": {
|
|
3177
|
-
"status": "
|
|
3178
|
-
"
|
|
3179
|
+
"status": "covered",
|
|
3180
|
+
"tools": [
|
|
3181
|
+
"conduyt_create_report"
|
|
3182
|
+
]
|
|
3179
3183
|
},
|
|
3180
3184
|
"GET /api/v1/reports/custom/:id": {
|
|
3181
3185
|
"status": "planned",
|
|
@@ -3190,14 +3194,40 @@
|
|
|
3190
3194
|
"reason": "additional report surfaces beyond ai/insights"
|
|
3191
3195
|
},
|
|
3192
3196
|
"POST /api/v1/reports/custom/:id/export": {
|
|
3193
|
-
"status": "
|
|
3194
|
-
"
|
|
3197
|
+
"status": "covered",
|
|
3198
|
+
"tools": [
|
|
3199
|
+
"conduyt_export_report"
|
|
3200
|
+
]
|
|
3195
3201
|
},
|
|
3196
3202
|
"POST /api/v1/reports/custom/:id/run": {
|
|
3197
3203
|
"status": "planned",
|
|
3198
3204
|
"reason": "additional report surfaces beyond ai/insights"
|
|
3199
3205
|
},
|
|
3206
|
+
"GET /api/v1/reports/custom/:id/subscriptions": {
|
|
3207
|
+
"status": "covered",
|
|
3208
|
+
"tools": [
|
|
3209
|
+
"conduyt_list_report_schedules"
|
|
3210
|
+
]
|
|
3211
|
+
},
|
|
3212
|
+
"POST /api/v1/reports/custom/:id/subscriptions": {
|
|
3213
|
+
"status": "covered",
|
|
3214
|
+
"tools": [
|
|
3215
|
+
"conduyt_create_report_schedule"
|
|
3216
|
+
]
|
|
3217
|
+
},
|
|
3218
|
+
"GET /api/v1/reports/custom/fields": {
|
|
3219
|
+
"status": "covered",
|
|
3220
|
+
"tools": [
|
|
3221
|
+
"conduyt_report_fields"
|
|
3222
|
+
]
|
|
3223
|
+
},
|
|
3200
3224
|
"POST /api/v1/reports/custom/preview": {
|
|
3225
|
+
"status": "covered",
|
|
3226
|
+
"tools": [
|
|
3227
|
+
"conduyt_preview_report"
|
|
3228
|
+
]
|
|
3229
|
+
},
|
|
3230
|
+
"GET /api/v1/reports/deliveries/:did/files/:index": {
|
|
3201
3231
|
"status": "planned",
|
|
3202
3232
|
"reason": "additional report surfaces beyond ai/insights"
|
|
3203
3233
|
},
|
|
@@ -3243,6 +3273,34 @@
|
|
|
3243
3273
|
"status": "planned",
|
|
3244
3274
|
"reason": "additional report surfaces beyond ai/insights"
|
|
3245
3275
|
},
|
|
3276
|
+
"GET /api/v1/reports/subscriptions/:sid": {
|
|
3277
|
+
"status": "planned",
|
|
3278
|
+
"reason": "additional report surfaces beyond ai/insights"
|
|
3279
|
+
},
|
|
3280
|
+
"PATCH /api/v1/reports/subscriptions/:sid": {
|
|
3281
|
+
"status": "covered",
|
|
3282
|
+
"tools": [
|
|
3283
|
+
"conduyt_update_report_schedule"
|
|
3284
|
+
]
|
|
3285
|
+
},
|
|
3286
|
+
"DELETE /api/v1/reports/subscriptions/:sid": {
|
|
3287
|
+
"status": "covered",
|
|
3288
|
+
"tools": [
|
|
3289
|
+
"conduyt_delete_report_schedule"
|
|
3290
|
+
]
|
|
3291
|
+
},
|
|
3292
|
+
"GET /api/v1/reports/subscriptions/:sid/deliveries": {
|
|
3293
|
+
"status": "covered",
|
|
3294
|
+
"tools": [
|
|
3295
|
+
"conduyt_report_schedule_deliveries"
|
|
3296
|
+
]
|
|
3297
|
+
},
|
|
3298
|
+
"POST /api/v1/reports/subscriptions/:sid/run": {
|
|
3299
|
+
"status": "covered",
|
|
3300
|
+
"tools": [
|
|
3301
|
+
"conduyt_send_report_schedule_now"
|
|
3302
|
+
]
|
|
3303
|
+
},
|
|
3246
3304
|
"GET /api/v1/reports/team": {
|
|
3247
3305
|
"status": "planned",
|
|
3248
3306
|
"reason": "additional report surfaces beyond ai/insights"
|