conduyt-mcp 4.54.0 → 4.56.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/bulk.js +10 -0
- package/dist/tools/lifecycle.js +17 -0
- package/package.json +1 -1
- package/scripts/coverage-matrix.json +31 -6
package/dist/tools/bulk.js
CHANGED
|
@@ -115,6 +115,16 @@ export function registerBulkTools(server, client) {
|
|
|
115
115
|
const result = await client.post("/api/v1/bulk/contacts/delete", params);
|
|
116
116
|
return formatResult(result);
|
|
117
117
|
});
|
|
118
|
+
server.tool("conduyt_bulk_delete_contacts_matching", "Soft-delete EVERY contact a list filter matches, across pages (#73, 2026-09-15) — the contacts page's \"Select all N matching\" action. FIRST CALL: pass the same filter you would give conduyt_list_contacts (search, tag, tags, source, reachability, company, company_id, assigned_to, created_from/to, updated_from/to, min_score, import_id, smart_view, smartListId, filters JSON, master_status, exclude_master_status, cf[key]) and expectedTotal = the meta.total that filter returned. The server re-counts and refuses when the set moved (409 expected_total_mismatch), refuses when some matching contacts are shared with you but not yours to delete (409 not_all_editable), then FREEZES the matching ids (max 50,000; 422 above) and deletes only those. Contacts with an upcoming appointment are kept (keptWithAppointments). If done=false the response carries remainingIds AND a signed continuation token: CALL AGAIN with ids = remainingIds and continuation = that token, verbatim (no filter, no expectedTotal) until done=true; busy=true means wait a moment first. The token is bound to you, this account and exactly those ids and expires after 2 hours (403 continuation_*); a continuation without it is refused. Empty filters are refused. Requires contacts:delete and an active subscription.", {
|
|
119
|
+
filter: z.record(z.string(), z.union([z.string(), z.array(z.string())])).optional().describe("First call: the contact list filter parameters, exactly as for conduyt_list_contacts; at least one non-empty value"),
|
|
120
|
+
expectedTotal: z.number().int().positive().optional().describe("First call: the total the list showed for this filter (meta.total)"),
|
|
121
|
+
ids: z.array(z.string().uuid()).min(1).max(50_000).optional().describe("Continuation: the remainingIds from the previous response, in the same order (send INSTEAD of filter/expectedTotal)"),
|
|
122
|
+
continuation: z.string().max(2_000).optional().describe("Continuation: the `continuation` token from the previous response, verbatim (required with ids)"),
|
|
123
|
+
}, async (params) => {
|
|
124
|
+
const body = params.ids ? { ids: params.ids, continuation: params.continuation } : { filter: params.filter, expectedTotal: params.expectedTotal };
|
|
125
|
+
const result = await client.post("/api/v1/bulk/contacts/delete-matching", body);
|
|
126
|
+
return formatResult(result);
|
|
127
|
+
});
|
|
118
128
|
server.tool("conduyt_bulk_dnc_contacts", "Add or remove a Do-Not-Contact / litigator mark for many contacts (compliance action). Requires the EXPLICIT 'dnc' API-key scope — an admin must grant it deliberately in Settings > API Keys (an unrestricted key does NOT imply it; without the scope this returns 403). Writes are also bound by contact-ownership rules for limited keys. To CHECK whether a number is already blocked, use conduyt_get_contact (dnc status) or the contacts dnc-status read.", {
|
|
119
129
|
contactIds: z.array(z.string()).describe("Contact UUIDs (those without a phone number are skipped)"),
|
|
120
130
|
action: z.enum(["add", "remove"]).describe("Add or remove the mark"),
|
package/dist/tools/lifecycle.js
CHANGED
|
@@ -105,6 +105,23 @@ export function registerLifecycleTools(server, client) {
|
|
|
105
105
|
.record(z.string().trim().min(1).max(40), z.string().trim().min(1).max(40))
|
|
106
106
|
.describe("Disposition value → master status, the complete map"),
|
|
107
107
|
}, async ({ statusMap }) => patchGeneralSettings(client, { callDispositions: { statusMap } }));
|
|
108
|
+
server.tool("conduyt_get_agent_statuses", "Read the account's curated dialer agent statuses (2026-09-15): Available, Break, Lunch, Meeting, Training, Offline. Each carries a FIXED basis the dialer runs on (available = takes inbound calls and Smart Dialing hand-outs; break = signed in, not ringable; offline = signed out), the tenant's label, and whether the tenant hid it from the softphone menu. Admins rename or hide with conduyt_update_agent_statuses; Available and Offline can never be hidden. Reports bucket by basis; heartbeats and status logs also carry the chosen code (statusCode).", {}, async () => formatResult(await client.get("/api/v1/dialer/agent-statuses")));
|
|
109
|
+
server.tool("conduyt_update_agent_statuses", "Rename or hide the curated dialer statuses (settings.agentStatuses). labels: code → the label reps see (1–24 chars; omit a code or pass an empty string to restore the default); hidden: the codes removed from the softphone menu (available and offline are refused with a 422; a rep already on a hidden status keeps it until they change). Codes: available, break, lunch, meeting, training, offline. Fields you omit keep their stored value (the tool reads the current settings first); two visible statuses cannot share a label (422). Requires settings:edit.", {
|
|
110
|
+
labels: z.record(z.string().trim().min(1).max(24), z.string().trim().max(24)).optional().describe("code → label; '' restores the default label for that code"),
|
|
111
|
+
hidden: z.array(z.string().trim().min(1).max(24)).max(6).optional().describe("the complete list of hidden codes (replaces the stored list when given)"),
|
|
112
|
+
}, async ({ labels, hidden }) => {
|
|
113
|
+
const current = (await client.get("/api/v1/settings"));
|
|
114
|
+
const stored = current?.data?.agentStatuses ?? {};
|
|
115
|
+
const nextLabels = { ...(stored.labels ?? {}) };
|
|
116
|
+
for (const [code, label] of Object.entries(labels ?? {})) {
|
|
117
|
+
if (label.trim())
|
|
118
|
+
nextLabels[code] = label.trim();
|
|
119
|
+
else
|
|
120
|
+
delete nextLabels[code];
|
|
121
|
+
}
|
|
122
|
+
const nextHidden = hidden ?? stored.hidden ?? [];
|
|
123
|
+
return patchGeneralSettings(client, { agentStatuses: { labels: nextLabels, hidden: nextHidden } });
|
|
124
|
+
});
|
|
108
125
|
server.tool("conduyt_update_master_statuses", "Set the account's custom lead lifecycle statuses. The five defaults (open, won, lost, abandoned, disqualified) always exist and cannot be removed; this REPLACES the custom list on top of them (max 20 names, 1-40 chars each, letters/numbers/spaces/-/_ only). Terminal statuses stop all outbound automation for a contact. Requires settings:edit.", {
|
|
109
126
|
statuses: z
|
|
110
127
|
.array(masterStatusName)
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-09-
|
|
3
|
-
"toolCount":
|
|
4
|
-
"totalEndpoints":
|
|
2
|
+
"generatedAt": "2026-09-15T16:44:24.985Z",
|
|
3
|
+
"toolCount": 264,
|
|
4
|
+
"totalEndpoints": 979,
|
|
5
5
|
"counts": {
|
|
6
|
-
"covered":
|
|
7
|
-
"excluded":
|
|
6
|
+
"covered": 247,
|
|
7
|
+
"excluded": 291,
|
|
8
8
|
"planned": 441,
|
|
9
9
|
"uncategorized": 0
|
|
10
10
|
},
|
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
"status": "excluded",
|
|
14
14
|
"reason": "unauthenticated liveness probe; trivially reachable without a tool"
|
|
15
15
|
},
|
|
16
|
+
"GET /api/public/account-emails/unsubscribe/:token": {
|
|
17
|
+
"status": "excluded",
|
|
18
|
+
"reason": "public unauthenticated surfaces (forms, booking, unsubscribe) — no key semantics"
|
|
19
|
+
},
|
|
20
|
+
"POST /api/public/account-emails/unsubscribe/:token": {
|
|
21
|
+
"status": "excluded",
|
|
22
|
+
"reason": "public unauthenticated surfaces (forms, booking, unsubscribe) — no key semantics"
|
|
23
|
+
},
|
|
16
24
|
"GET /api/public/appointments/:token": {
|
|
17
25
|
"status": "excluded",
|
|
18
26
|
"reason": "public unauthenticated surfaces (forms, booking, unsubscribe) — no key semantics"
|
|
@@ -864,6 +872,12 @@
|
|
|
864
872
|
"conduyt_bulk_delete_contacts"
|
|
865
873
|
]
|
|
866
874
|
},
|
|
875
|
+
"POST /api/v1/bulk/contacts/delete-matching": {
|
|
876
|
+
"status": "covered",
|
|
877
|
+
"tools": [
|
|
878
|
+
"conduyt_bulk_delete_contacts_matching"
|
|
879
|
+
]
|
|
880
|
+
},
|
|
867
881
|
"POST /api/v1/bulk/contacts/dnc": {
|
|
868
882
|
"status": "covered",
|
|
869
883
|
"tools": [
|
|
@@ -1851,6 +1865,10 @@
|
|
|
1851
1865
|
"status": "excluded",
|
|
1852
1866
|
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
1853
1867
|
},
|
|
1868
|
+
"GET /api/v1/cron/trial-lifecycle-emails": {
|
|
1869
|
+
"status": "excluded",
|
|
1870
|
+
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
1871
|
+
},
|
|
1854
1872
|
"GET /api/v1/cron/up-for-grabs-expire": {
|
|
1855
1873
|
"status": "excluded",
|
|
1856
1874
|
"reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
|
|
@@ -2081,6 +2099,12 @@
|
|
|
2081
2099
|
"status": "planned",
|
|
2082
2100
|
"reason": "deal subroutes beyond core CRUD (history, files, links)"
|
|
2083
2101
|
},
|
|
2102
|
+
"GET /api/v1/dialer/agent-statuses": {
|
|
2103
|
+
"status": "covered",
|
|
2104
|
+
"tools": [
|
|
2105
|
+
"conduyt_get_agent_statuses"
|
|
2106
|
+
]
|
|
2107
|
+
},
|
|
2084
2108
|
"GET /api/v1/dialer/agents-status": {
|
|
2085
2109
|
"status": "excluded",
|
|
2086
2110
|
"reason": "realtime browser dialer (WebRTC tokens, live call control) — needs a live audio session"
|
|
@@ -3582,7 +3606,8 @@
|
|
|
3582
3606
|
"tools": [
|
|
3583
3607
|
"conduyt_get_contact_table_defaults",
|
|
3584
3608
|
"conduyt_set_contact_table_defaults",
|
|
3585
|
-
"conduyt_get_lifecycle_settings"
|
|
3609
|
+
"conduyt_get_lifecycle_settings",
|
|
3610
|
+
"conduyt_update_agent_statuses"
|
|
3586
3611
|
]
|
|
3587
3612
|
},
|
|
3588
3613
|
"PATCH /api/v1/settings": {
|