conduyt-mcp 4.27.0 → 4.29.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.
@@ -137,6 +137,41 @@ export function registerAiExtendedTools(server, client) {
137
137
  const result = await client.get(`/api/v1/ai/agenda${qs ? `?${qs}` : ""}`);
138
138
  return formatResult(result);
139
139
  });
140
+ server.tool("conduyt_ai_plan_actions", "Copilot actions — PLAN. Turn a natural-language request ('call Luis back tomorrow at 10', 'note on Ana: prefers texts', 'move the Roof deal to Proposal sent', 'draft a text to Ana about the quote') into a preview of concrete CRM actions resolved against records the caller can see (tasks, callbacks, notes, appointments, existing tags, deal stage moves, SMS/email DRAFTS). Nothing is written by this call. Returns the resolved actions (ready or with an issue such as an ambiguous name) and a single-use confirmationToken bound to that exact set; pass the actions array back UNMODIFIED with the token to conduyt_ai_execute_actions to apply. Drafts are never sent by the system.", {
141
+ message: z.string().min(1).max(2000).describe("What the rep wants done, in plain language"),
142
+ contactId: z.string().optional().describe("Contact UUID the request refers to when it says 'this contact' (must be visible to the caller)"),
143
+ dealId: z.string().optional().describe("Deal UUID the request refers to when it says 'this deal'"),
144
+ }, async ({ message, contactId, dealId }) => {
145
+ const section = contactId ? { type: "contact", id: contactId } : dealId ? { type: "deal", id: dealId } : undefined;
146
+ const result = await client.post("/api/v1/ai/actions", { message, ...(section ? { section } : {}) });
147
+ return formatResult(result);
148
+ });
149
+ server.tool("conduyt_ai_execute_actions", "Copilot actions — APPLY. Executes the ready actions from a conduyt_ai_plan_actions result through the same routes the UI uses (permissions, tenant scope, visibility, audit all apply). Send the plan's `actions` array back exactly as received plus its confirmationToken (single-use, expires in 5 minutes, bound to the caller and the exact set — any edit is refused). Optionally narrow with selectedIds. Returns a per-action result; drafts are not sent. Sending the same token again REPLAYS the run (nothing runs twice; the response says replayed:true). A result with inFlight:true was started but never confirmed — inspect the record, then resume it explicitly with resumeIds.", {
150
+ actions: z.array(z.record(z.unknown())).min(1).max(8).describe("The `actions` array from the plan, unmodified"),
151
+ confirmationToken: z.string().describe("The plan's confirmationToken"),
152
+ selectedIds: z.array(z.string()).optional().describe("First apply only: apply just these action ids (default: every ready action). A later call with the same token must resend the same selection — a run's action set cannot be widened."),
153
+ resumeIds: z.array(z.string()).optional().describe("Re-run actions the previous response reported with inFlight:true (started but never confirmed, e.g. the connection dropped mid-apply). Check the record first — the write may already exist — and only after the result's resumableAt (the earlier attempt can no longer be running); earlier resumes are refused. Later actions wait behind an in-flight one (plan order). Never re-run automatically."),
154
+ }, async ({ actions, confirmationToken, selectedIds, resumeIds }) => {
155
+ const result = await client.post("/api/v1/ai/actions/execute", { actions, confirmationToken, ...(selectedIds ? { selectedIds } : {}), ...(resumeIds ? { resumeIds } : {}) });
156
+ return formatResult(result);
157
+ });
158
+ server.tool("conduyt_reply_suggestion", "Copilot Tier 2 — the suggested reply for a contact's LATEST inbound text or email (what they said, what they are asking, sentiment, and a draft reply shaped for the channel). Advice only: nothing is sent by this tool. state: ready | pending (the copilot has not read it yet) | none | decided | skipped (opt-out, do-not-contact, feature off).", {
159
+ contactId: z.string().describe("Contact UUID (must be visible to the caller)"),
160
+ }, async ({ contactId }) => {
161
+ const result = await client.get(`/api/v1/conversations/${encodeURIComponent(contactId)}/suggestion`);
162
+ return formatResult(result);
163
+ });
164
+ server.tool("conduyt_reply_suggestion_decide", "Copilot Tier 2 — record what was done with a suggested reply: accepted (the draft was taken into a composer), sent (with the outbound message id, after sending it through conduyt_send_message / the messages API), or dismissed. A suggestion moves once (accepted → sent is allowed).", {
165
+ contactId: z.string().describe("Contact UUID"),
166
+ messageId: z.string().describe("The inbound message the suggestion answers (suggestion.messageId)"),
167
+ decision: z.enum(["accepted", "sent", "dismissed"]),
168
+ sentMessageId: z.string().optional().describe("REQUIRED with decision=sent: the OUTBOUND message id that was sent — it must belong to this contact and be provider-confirmed (status sent/delivered, or queued with a provider id); a pending, draft or failed message is refused"),
169
+ }, async ({ contactId, messageId, decision, sentMessageId }) => {
170
+ if (decision === "sent" && !sentMessageId)
171
+ throw new Error("decision=sent requires sentMessageId (the confirmed outbound message that was sent)");
172
+ const result = await client.post(`/api/v1/conversations/${encodeURIComponent(contactId)}/suggestion`, { messageId, decision, ...(sentMessageId ? { sentMessageId } : {}) });
173
+ return formatResult(result);
174
+ });
140
175
  server.tool("conduyt_ai_daily_brief", "AI-generated daily brief for the current user. Summarizes today's priorities — deals needing attention, overdue tasks, hot leads, and recommended focus.", {}, async () => {
141
176
  const result = await client.get("/api/v1/ai/daily-brief");
142
177
  return formatResult(result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.27.0",
3
+ "version": "4.29.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-06T04:07:11.872Z",
3
- "toolCount": 210,
4
- "totalEndpoints": 912,
2
+ "generatedAt": "2026-09-06T14:27:53.877Z",
3
+ "toolCount": 214,
4
+ "totalEndpoints": 918,
5
5
  "counts": {
6
- "covered": 194,
7
- "excluded": 274,
8
- "planned": 444,
6
+ "covered": 198,
7
+ "excluded": 275,
8
+ "planned": 445,
9
9
  "uncategorized": 0
10
10
  },
11
11
  "matrix": {
@@ -309,6 +309,22 @@
309
309
  "status": "excluded",
310
310
  "reason": "super-admin platform operations (Conduyt staff only)"
311
311
  },
312
+ "POST /api/v1/ai/actions": {
313
+ "status": "covered",
314
+ "tools": [
315
+ "conduyt_ai_plan_actions"
316
+ ]
317
+ },
318
+ "POST /api/v1/ai/actions/execute": {
319
+ "status": "covered",
320
+ "tools": [
321
+ "conduyt_ai_execute_actions"
322
+ ]
323
+ },
324
+ "GET /api/v1/ai/actions/runs/:id": {
325
+ "status": "planned",
326
+ "reason": "AI surfaces beyond chat/insights/tasks tools"
327
+ },
312
328
  "GET /api/v1/ai/agenda": {
313
329
  "status": "planned",
314
330
  "reason": "AI surfaces beyond chat/insights/tasks tools"
@@ -1457,6 +1473,18 @@
1457
1473
  "status": "planned",
1458
1474
  "reason": "conversation thread surfaces"
1459
1475
  },
1476
+ "GET /api/v1/conversations/:contactId/suggestion": {
1477
+ "status": "covered",
1478
+ "tools": [
1479
+ "conduyt_reply_suggestion"
1480
+ ]
1481
+ },
1482
+ "POST /api/v1/conversations/:contactId/suggestion": {
1483
+ "status": "covered",
1484
+ "tools": [
1485
+ "conduyt_reply_suggestion_decide"
1486
+ ]
1487
+ },
1460
1488
  "GET /api/v1/conversations/export": {
1461
1489
  "status": "planned",
1462
1490
  "reason": "conversation thread surfaces"
@@ -1621,6 +1649,10 @@
1621
1649
  "status": "excluded",
1622
1650
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1623
1651
  },
1652
+ "GET /api/v1/cron/process-message-insights": {
1653
+ "status": "excluded",
1654
+ "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"
1655
+ },
1624
1656
  "GET /api/v1/cron/process-outbox": {
1625
1657
  "status": "excluded",
1626
1658
  "reason": "CRON_SECRET-gated infrastructure jobs; never key-callable"