conduyt-mcp 4.51.0 → 4.52.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
@@ -42,6 +42,7 @@ 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
44
  import { registerWhatsappTools } from "./tools/whatsapp.js";
45
+ import { registerFromkTools } from "./tools/fromk.js";
45
46
  import { registerReportTools } from "./tools/reports.js";
46
47
  import { registerReportDashboardTools } from "./tools/dashboards.js";
47
48
  const apiUrl = process.env.CONDUYT_API_URL;
@@ -108,6 +109,7 @@ registerReplyCaptureTools(server, client);
108
109
  registerCallFlowTools(server, client);
109
110
  registerProjectBlueTools(server, client);
110
111
  registerWhatsappTools(server, client);
112
+ registerFromkTools(server, client);
111
113
  registerReportTools(server, client);
112
114
  registerReportDashboardTools(server, client);
113
115
  const transport = new StdioServerTransport();
@@ -0,0 +1,10 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ConduytClient } from "../client.js";
3
+ /**
4
+ * FromK.ai (2026-09-14): Conduyt's own texting platform as a native SMS transport. The tenant's FromK.ai workspace
5
+ * texts (its lines, its credits, its consent and STOP rules); Conduyt drives it with the workspace's API key. These
6
+ * tools cover what an AI operator needs: is it connected, which lines are send-ready (the fromkPhoneNumberId a step or
7
+ * conduyt_send_sms picks), refreshing that list, connecting or replacing the key, storing the webhook signing secret,
8
+ * and disconnecting.
9
+ */
10
+ export declare function registerFromkTools(server: McpServer, client: ConduytClient): void;
@@ -0,0 +1,45 @@
1
+ import { z } from "zod";
2
+ import { formatResult } from "../client.js";
3
+ /**
4
+ * FromK.ai (2026-09-14): Conduyt's own texting platform as a native SMS transport. The tenant's FromK.ai workspace
5
+ * texts (its lines, its credits, its consent and STOP rules); Conduyt drives it with the workspace's API key. These
6
+ * tools cover what an AI operator needs: is it connected, which lines are send-ready (the fromkPhoneNumberId a step or
7
+ * conduyt_send_sms picks), refreshing that list, connecting or replacing the key, storing the webhook signing secret,
8
+ * and disconnecting.
9
+ */
10
+ export function registerFromkTools(server, client) {
11
+ server.tool("conduyt_fromk_status", "FromK.ai connection for this account (GET /api/v1/settings/fromk): status connected | not_configured, the API key masked, the lines snapshot (phoneNumberId, phoneE164, status, provider, sendReady) and the webhook setup (the URL the tenant pastes into FromK.ai → Developers → Webhooks, whether the signing secret is stored, the events to subscribe). Requires settings:view.", {}, async () => {
12
+ const result = await client.get("/api/v1/settings/fromk");
13
+ return formatResult(result);
14
+ });
15
+ server.tool("conduyt_fromk_lines", "Refresh the FromK.ai line list from the workspace with the stored key and store it as the new snapshot (GET /api/v1/settings/fromk/lines): every line with sendReady (assigned, ownership verified, SMS-capable, on a real carrier). A send-ready phoneNumberId is a valid send_sms config.fromkPhoneNumberId / conduyt_send_sms fromkPhoneNumberId. 409 fromk_not_configured, 422 key_revoked, 502 vendor_unavailable (the old snapshot is kept). Requires settings:view.", {}, async () => {
16
+ const result = await client.get("/api/v1/settings/fromk/lines");
17
+ return formatResult(result);
18
+ });
19
+ server.tool("conduyt_fromk_connect", "Connect or replace the account's FromK.ai workspace API key (PUT /api/v1/settings/fromk): fk_… with numbers:read, contacts:write and messages:send. Verified with FromK.ai before it is stored (it must list the workspace's lines: 422 key_rejected / key_scope, 502 vendor_unavailable, nothing stored). Pass disconnect: true to forget the key, the signing secret, the lines and every contact link instead (DELETE): workflow steps on the FromK.ai channel stop sending. Requires settings:edit.", {
20
+ apiKey: z.string().min(24).max(200).optional().describe("FromK.ai workspace API key (fk_…); required unless disconnect is true"),
21
+ disconnect: z.boolean().optional().describe("true = forget everything FromK.ai (DELETE)"),
22
+ }, async ({ apiKey, disconnect }) => {
23
+ if (disconnect) {
24
+ const result = await client.del("/api/v1/settings/fromk");
25
+ return formatResult(result);
26
+ }
27
+ if (!apiKey)
28
+ throw new Error("apiKey is required unless disconnect is true");
29
+ const result = await client.put("/api/v1/settings/fromk", { apiKey });
30
+ return formatResult(result);
31
+ });
32
+ server.tool("conduyt_fromk_webhook_secret", "Store (PUT /api/v1/settings/fromk/webhook) or forget (DELETE, clear: true) the signing secret of the FromK.ai webhook endpoint the tenant pointed at Conduyt (whsec_…, shown once by FromK.ai when the endpoint is created). Until it is stored, FromK.ai replies and delivery reports are refused. 409 fromk_not_configured before the key is connected. Requires settings:edit.", {
33
+ secret: z.string().regex(/^whsec_[A-Za-z0-9_-]{16,}$/).optional().describe("the endpoint's signing secret; required unless clear is true"),
34
+ clear: z.boolean().optional().describe("true = forget the stored secret (DELETE)"),
35
+ }, async ({ secret, clear }) => {
36
+ if (clear) {
37
+ const result = await client.del("/api/v1/settings/fromk/webhook");
38
+ return formatResult(result);
39
+ }
40
+ if (!secret)
41
+ throw new Error("secret is required unless clear is true");
42
+ const result = await client.put("/api/v1/settings/fromk/webhook", { secret });
43
+ return formatResult(result);
44
+ });
45
+ }
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { formatResult } from "../client.js";
3
3
  export function registerMessagingTools(server, client) {
4
- server.tool("conduyt_sms_transports", "What a workflow Send SMS step can speak through TODAY (GET /api/v1/sms/transports): twilio {connected, numbers[] = the account number + every active rep's assigned line, each a valid send_sms config.fromNumber}, projectBlue {connected, lines[] = Project Blue iPhone lines held by active reps, each lineId a valid config.projectBlueLineId for smsMode project_blue}, whatsapp {connected, sender = the account's WhatsApp sender for smsMode whatsapp; templates come from conduyt_whatsapp_templates}, voiceDrop {connected = Twilio is connected, recordings {hasDefault, templates[] = names valid as config.voiceDropRecording for smsMode voice_drop}}, webhook {providers = active webhook providers for smsMode webhook}, fromk {status: coming_soon}. Use it before building or editing a send_sms step so you never configure a transport that is not connected. Requires automations:view.", {}, async () => {
4
+ server.tool("conduyt_sms_transports", "What a workflow Send SMS step can speak through TODAY (GET /api/v1/sms/transports): twilio {connected, numbers[] = the account number + every active rep's assigned line, each a valid send_sms config.fromNumber}, projectBlue {connected, lines[] = Project Blue iPhone lines held by active reps, each lineId a valid config.projectBlueLineId for smsMode project_blue}, whatsapp {connected, sender = the account's WhatsApp sender for smsMode whatsapp; templates come from conduyt_whatsapp_templates}, voiceDrop {connected = Twilio is connected, recordings {hasDefault, templates[] = names valid as config.voiceDropRecording for smsMode voice_drop}}, webhook {providers = active webhook providers for smsMode webhook}, fromk {status connected | setup, connected, lines[] = the FromK.ai workspace's send-ready lines, each phoneNumberId a valid config.fromkPhoneNumberId for smsMode fromk}. Use it before building or editing a send_sms step so you never configure a transport that is not connected. Requires automations:view.", {}, async () => {
5
5
  const result = await client.get("/api/v1/sms/transports");
6
6
  return formatResult(result);
7
7
  });
@@ -22,7 +22,8 @@ export function registerMessagingTools(server, client) {
22
22
  contactId: z.string().describe("Contact UUID — must have a phone number on file"),
23
23
  body: z.string().max(1600).optional().describe("Message text (max 1600 characters). Required unless a WhatsApp template is sent (transport whatsapp + whatsappContentSid): then the template Twilio renders is the text, and a media-only template has none."),
24
24
  fromNumber: z.string().optional().describe("Sender phone number (defaults to account's primary number)"),
25
- transport: z.enum(["twilio", "project_blue", "whatsapp"]).optional().describe("'project_blue' sends from the acting user's own Project Blue iPhone line (iMessage when the lead is on Apple, SMS otherwise; the user must have a line assigned in Settings → Project Blue). 'whatsapp' sends from the account's WhatsApp sender (Settings → WhatsApp): free-form text only within 24 hours of the contact's last WhatsApp message (422 whatsapp_window_closed otherwise), or an approved template via whatsappContentSid. Default: Twilio."),
25
+ transport: z.enum(["twilio", "project_blue", "whatsapp", "fromk"]).optional().describe("'project_blue' sends from the acting user's own Project Blue iPhone line (iMessage when the lead is on Apple, SMS otherwise; the user must have a line assigned in Settings → Project Blue). 'whatsapp' sends from the account's WhatsApp sender (Settings → WhatsApp): free-form text only within 24 hours of the contact's last WhatsApp message (422 whatsapp_window_closed otherwise), or an approved template via whatsappContentSid. 'fromk' sends through the account's FromK.ai workspace (Settings → FromK.ai) from fromkPhoneNumberId, else the workspace's single send-ready line (422 fromk_line_required with several). Default: Twilio."),
26
+ fromkPhoneNumberId: z.string().uuid().optional().describe("transport fromk: the FromK.ai line to send from (a send-ready phoneNumberId from conduyt_fromk_status / conduyt_sms_transports fromk.lines); omit when the workspace has one send-ready line"),
26
27
  whatsappContentSid: z.string().regex(/^HX[0-9a-fA-F]{32}$/).optional().describe("transport whatsapp: an APPROVED WhatsApp template (Twilio Content SID from conduyt_whatsapp_templates); Conduyt reads the template from Twilio at send time, records its text with the variables in place, and refuses an unlisted (whatsapp_template_invalid) or unapproved (whatsapp_template_not_approved) one. body is still required but the template's text is what is sent and recorded."),
27
28
  whatsappContentVariables: z.record(z.string().regex(/^\d+$/), z.string().max(1024)).optional().describe("transport whatsapp with a template: placeholder number → value, e.g. { \"1\": \"Alex\" }"),
28
29
  idempotencyKey: z.string().min(8).max(200).optional().describe("Operation key for at-most-once sends: reuse the SAME key when retrying a send whose outcome you did not see; a settled key answers with the original message and never sends twice."),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.51.0",
3
+ "version": "4.52.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,10 +1,10 @@
1
1
  {
2
- "generatedAt": "2026-09-11T22:38:09.066Z",
3
- "toolCount": 256,
4
- "totalEndpoints": 965,
2
+ "generatedAt": "2026-09-14T15:23:41.003Z",
3
+ "toolCount": 260,
4
+ "totalEndpoints": 972,
5
5
  "counts": {
6
- "covered": 239,
7
- "excluded": 285,
6
+ "covered": 245,
7
+ "excluded": 286,
8
8
  "planned": 441,
9
9
  "uncategorized": 0
10
10
  },
@@ -3623,6 +3623,42 @@
3623
3623
  "status": "planned",
3624
3624
  "reason": "account settings read/update surfaces"
3625
3625
  },
3626
+ "GET /api/v1/settings/fromk": {
3627
+ "status": "covered",
3628
+ "tools": [
3629
+ "conduyt_fromk_status"
3630
+ ]
3631
+ },
3632
+ "PUT /api/v1/settings/fromk": {
3633
+ "status": "covered",
3634
+ "tools": [
3635
+ "conduyt_fromk_connect"
3636
+ ]
3637
+ },
3638
+ "DELETE /api/v1/settings/fromk": {
3639
+ "status": "covered",
3640
+ "tools": [
3641
+ "conduyt_fromk_connect"
3642
+ ]
3643
+ },
3644
+ "GET /api/v1/settings/fromk/lines": {
3645
+ "status": "covered",
3646
+ "tools": [
3647
+ "conduyt_fromk_lines"
3648
+ ]
3649
+ },
3650
+ "PUT /api/v1/settings/fromk/webhook": {
3651
+ "status": "covered",
3652
+ "tools": [
3653
+ "conduyt_fromk_webhook_secret"
3654
+ ]
3655
+ },
3656
+ "DELETE /api/v1/settings/fromk/webhook": {
3657
+ "status": "covered",
3658
+ "tools": [
3659
+ "conduyt_fromk_webhook_secret"
3660
+ ]
3661
+ },
3626
3662
  "POST /api/v1/settings/integrations/:id/test": {
3627
3663
  "status": "planned",
3628
3664
  "reason": "account settings read/update surfaces"
@@ -4246,6 +4282,10 @@
4246
4282
  "status": "planned",
4247
4283
  "reason": "outbound webhook delivery replay — agent-relevant"
4248
4284
  },
4285
+ "POST /api/v1/webhooks/sms/fromk/:accountId": {
4286
+ "status": "excluded",
4287
+ "reason": "inbound provider callback receivers (Twilio/Resend/etc.), not client-callable"
4288
+ },
4249
4289
  "POST /api/v1/webhooks/sms/inbound": {
4250
4290
  "status": "excluded",
4251
4291
  "reason": "inbound provider callback receivers (Twilio/Resend/etc.), not client-callable"