conduyt-mcp 4.57.0 → 4.58.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
@@ -44,6 +44,8 @@ import { registerCallFlowTools } from "./tools/call-flows.js";
44
44
  import { registerProjectBlueTools } from "./tools/project-blue.js";
45
45
  import { registerWhatsappTools } from "./tools/whatsapp.js";
46
46
  import { registerFromkTools } from "./tools/fromk.js";
47
+ import { registerZoomTools } from "./tools/zoom.js";
48
+ import { registerBookingPageTools } from "./tools/booking-pages.js";
47
49
  import { registerReportTools } from "./tools/reports.js";
48
50
  import { registerReportDashboardTools } from "./tools/dashboards.js";
49
51
  const apiUrl = process.env.CONDUYT_API_URL;
@@ -112,6 +114,8 @@ registerCallFlowTools(server, client);
112
114
  registerProjectBlueTools(server, client);
113
115
  registerWhatsappTools(server, client);
114
116
  registerFromkTools(server, client);
117
+ registerZoomTools(server, client);
118
+ registerBookingPageTools(server, client);
115
119
  registerReportTools(server, client);
116
120
  registerReportDashboardTools(server, client);
117
121
  const transport = new StdioServerTransport();
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ConduytClient } from "../client.js";
3
+ export declare function registerBookingPageTools(server: McpServer, client: ConduytClient): void;
@@ -0,0 +1,47 @@
1
+ import { z } from "zod";
2
+ import { formatResult } from "../client.js";
3
+ /**
4
+ * Booking pages (Conduyt #72, 2026-09-15): the public scheduling pages. Until now an AI operator could read appointments
5
+ * but not the pages that create them; these tools cover the page itself and its video option (videoProvider "zoom" =
6
+ * one Zoom meeting per booking, see conduyt_zoom_connect).
7
+ */
8
+ const availabilityWindow = z.object({
9
+ dayOfWeek: z.number().int().min(0).max(6).describe("0 = Sunday … 6 = Saturday"),
10
+ startTime: z.string().regex(/^\d{2}:\d{2}$/).describe("HH:MM in the booking timezone"),
11
+ endTime: z.string().regex(/^\d{2}:\d{2}$/).describe("HH:MM in the booking timezone"),
12
+ });
13
+ const pageFields = {
14
+ title: z.string().min(1).max(200).optional().describe("page title"),
15
+ slug: z.string().min(1).max(100).optional().describe("public slug (conduyt.app/book/<slug>)"),
16
+ description: z.string().max(2000).nullable().optional(),
17
+ userId: z.string().uuid().optional().describe("host user UUID (the page owner)"),
18
+ durationMinutes: z.number().int().min(5).max(480).optional(),
19
+ minimumNotice: z.number().int().min(0).optional().describe("earliest booking, minutes from now"),
20
+ maxAdvanceDays: z.number().int().min(0).optional().describe("how far ahead visitors may book"),
21
+ location: z.string().max(500).nullable().optional().describe("written location (link, phone, address); with videoProvider zoom it is the fallback when Zoom is down"),
22
+ videoProvider: z.enum(["zoom"]).nullable().optional().describe("'zoom' = a new Zoom meeting for every booking (needs conduyt_zoom_connect); null = the written location only"),
23
+ videoHostEmail: z.string().email().nullable().optional().describe("the Zoom user the meeting is created under (a user of the connected Zoom account); null = the app's own user"),
24
+ leadSource: z.string().max(100).nullable().optional().describe("lead source stamped on contacts the page creates"),
25
+ brandName: z.string().max(100).nullable().optional().describe("white-label name shown on the page and in Zoom meeting names"),
26
+ embedAllowedOrigins: z.array(z.string().url()).nullable().optional().describe("origins allowed to embed/call the page; null or empty = any"),
27
+ availabilityRules: z.array(availabilityWindow).nullable().optional().describe("availability override; null = the host's own rules"),
28
+ isActive: z.boolean().optional(),
29
+ };
30
+ export function registerBookingPageTools(server, client) {
31
+ server.tool("conduyt_list_booking_pages", "List the account's booking pages (GET /api/v1/booking-pages): slug, title, host, duration, active, video option. Requires calendar:view.", { activeOnly: z.boolean().optional().describe("only pages accepting bookings") }, async ({ activeOnly }) => {
32
+ const result = await client.get(`/api/v1/booking-pages?${activeOnly ? "is_active=true" : ""}`);
33
+ return formatResult(result);
34
+ });
35
+ server.tool("conduyt_get_booking_page", "One booking page with every setting (GET /api/v1/booking-pages/{id}). Requires calendar:view.", { id: z.string().uuid().describe("booking page UUID") }, async ({ id }) => {
36
+ const result = await client.get(`/api/v1/booking-pages/${id}`);
37
+ return formatResult(result);
38
+ });
39
+ server.tool("conduyt_create_booking_page", "Create a booking page (POST /api/v1/booking-pages): title, slug, userId (host) and durationMinutes at least; optional written location, videoProvider 'zoom' + videoHostEmail for one Zoom meeting per booking, availabilityRules override, embedAllowedOrigins, leadSource, brandName. Requires calendar:edit (admin/owner).", { ...pageFields, title: z.string().min(1).max(200), slug: z.string().min(1).max(100), userId: z.string().uuid(), durationMinutes: z.number().int().min(5).max(480) }, async (params) => {
40
+ const result = await client.post("/api/v1/booking-pages", params);
41
+ return formatResult(result);
42
+ });
43
+ server.tool("conduyt_update_booking_page", "Update a booking page (PATCH /api/v1/booking-pages/{id}): only the fields given change. videoProvider 'zoom' turns on one Zoom meeting per booking (null turns it off and clears the host); videoHostEmail must be a user of the connected Zoom account (null = the app's own user). Requires calendar:edit (admin/owner).", { id: z.string().uuid().describe("booking page UUID"), ...pageFields }, async ({ id, ...updates }) => {
44
+ const result = await client.patch(`/api/v1/booking-pages/${id}`, updates);
45
+ return formatResult(result);
46
+ });
47
+ }
@@ -0,0 +1,9 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ConduytClient } from "../client.js";
3
+ /**
4
+ * Zoom (Conduyt #72, 2026-09-15): the tenant's Zoom Server-to-Server OAuth app as a Conduyt connection. Booking pages
5
+ * with videoProvider "zoom" create one Zoom meeting per booking (the join link is the appointment's location, rides the
6
+ * confirmation email and the host's calendar; cancellations delete it, reschedules move it). These tools cover what an
7
+ * AI operator needs: is it connected, connect/replace/disconnect the app, and prove it (plus a chosen host) works.
8
+ */
9
+ export declare function registerZoomTools(server: McpServer, client: ConduytClient): void;
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ import { formatResult } from "../client.js";
3
+ /**
4
+ * Zoom (Conduyt #72, 2026-09-15): the tenant's Zoom Server-to-Server OAuth app as a Conduyt connection. Booking pages
5
+ * with videoProvider "zoom" create one Zoom meeting per booking (the join link is the appointment's location, rides the
6
+ * confirmation email and the host's calendar; cancellations delete it, reschedules move it). These tools cover what an
7
+ * AI operator needs: is it connected, connect/replace/disconnect the app, and prove it (plus a chosen host) works.
8
+ */
9
+ export function registerZoomTools(server, client) {
10
+ server.tool("conduyt_zoom_status", "Zoom connection for this account (GET /api/v1/settings/zoom): status connected | not_configured, the Zoom account id and client id masked (the client secret is never returned). Requires settings:view.", {}, async () => {
11
+ const result = await client.get("/api/v1/settings/zoom");
12
+ return formatResult(result);
13
+ });
14
+ server.tool("conduyt_zoom_connect", "Connect or replace the account's Zoom Server-to-Server OAuth app (PUT /api/v1/settings/zoom): accountId, clientId, clientSecret from the Zoom App Marketplace app with scopes meeting:write/read/update/delete:meeting:admin + user:read:user:admin. Verified with Zoom before it is stored (a token must be issued and the app's own user readable: 422 credentials_rejected / scope_missing, 502 vendor_unavailable, nothing stored). Answers the app's user = the default meeting host. Pass disconnect: true to forget the app instead (DELETE): booking pages set to Zoom fall back to their written location. Requires settings:edit.", {
15
+ accountId: z.string().min(8).max(64).optional().describe("Zoom account id of the S2S app; required unless disconnect is true"),
16
+ clientId: z.string().min(8).max(64).optional().describe("Zoom client id; required unless disconnect is true"),
17
+ clientSecret: z.string().min(16).max(200).optional().describe("Zoom client secret; required unless disconnect is true"),
18
+ disconnect: z.boolean().optional().describe("true = forget the stored app (DELETE)"),
19
+ force: z.boolean().optional().describe("replace or disconnect although upcoming bookings still hold Zoom meetings the current app created (409 zoom_meetings_active otherwise); those meetings then stay on the old Zoom account and cancellations record zoom_orphaned"),
20
+ }, async ({ accountId, clientId, clientSecret, disconnect, force }) => {
21
+ if (disconnect) {
22
+ const result = await client.del(`/api/v1/settings/zoom?${force ? "force=1" : ""}`);
23
+ return formatResult(result);
24
+ }
25
+ if (!accountId || !clientId || !clientSecret)
26
+ throw new Error("accountId, clientId and clientSecret are required unless disconnect is true");
27
+ const result = await client.put("/api/v1/settings/zoom", { accountId, clientId, clientSecret, ...(force ? { force: true } : {}) });
28
+ return formatResult(result);
29
+ });
30
+ server.tool("conduyt_zoom_test", "Prove the stored Zoom app still works (POST /api/v1/settings/zoom/test): a token is issued and the app's own user answers; pass hostEmail to also prove that user belongs to the Zoom account (the value a booking page's videoHostEmail must be). Read-only on Zoom, nothing is created. 200 with ok:false + code host_not_found | credentials_rejected | scope_missing | vendor_unavailable when it does not; 409 zoom_not_configured before connect. Requires settings:view.", {
31
+ hostEmail: z.string().email().optional().describe("a Zoom user of the connected account to verify as a meeting host"),
32
+ }, async ({ hostEmail }) => {
33
+ const result = await client.post("/api/v1/settings/zoom/test", { hostEmail: hostEmail ?? undefined });
34
+ return formatResult(result);
35
+ });
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conduyt-mcp",
3
- "version": "4.57.0",
3
+ "version": "4.58.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-15T16:44:24.985Z",
3
- "toolCount": 268,
4
- "totalEndpoints": 979,
2
+ "generatedAt": "2026-09-16T02:16:31.092Z",
3
+ "toolCount": 275,
4
+ "totalEndpoints": 983,
5
5
  "counts": {
6
- "covered": 249,
6
+ "covered": 257,
7
7
  "excluded": 291,
8
- "planned": 439,
8
+ "planned": 435,
9
9
  "uncategorized": 0
10
10
  },
11
11
  "matrix": {
@@ -791,20 +791,28 @@
791
791
  "reason": "the API itself rejects key-based billing management (interactive session required)"
792
792
  },
793
793
  "GET /api/v1/booking-pages": {
794
- "status": "planned",
795
- "reason": "booking page management"
794
+ "status": "covered",
795
+ "tools": [
796
+ "conduyt_list_booking_pages"
797
+ ]
796
798
  },
797
799
  "POST /api/v1/booking-pages": {
798
- "status": "planned",
799
- "reason": "booking page management"
800
+ "status": "covered",
801
+ "tools": [
802
+ "conduyt_create_booking_page"
803
+ ]
800
804
  },
801
805
  "GET /api/v1/booking-pages/:id": {
802
- "status": "planned",
803
- "reason": "booking page management"
806
+ "status": "covered",
807
+ "tools": [
808
+ "conduyt_get_booking_page"
809
+ ]
804
810
  },
805
811
  "PATCH /api/v1/booking-pages/:id": {
806
- "status": "planned",
807
- "reason": "booking page management"
812
+ "status": "covered",
813
+ "tools": [
814
+ "conduyt_update_booking_page"
815
+ ]
808
816
  },
809
817
  "DELETE /api/v1/booking-pages/:id": {
810
818
  "status": "planned",
@@ -3840,6 +3848,30 @@
3840
3848
  "conduyt_whatsapp_senders"
3841
3849
  ]
3842
3850
  },
3851
+ "GET /api/v1/settings/zoom": {
3852
+ "status": "covered",
3853
+ "tools": [
3854
+ "conduyt_zoom_status"
3855
+ ]
3856
+ },
3857
+ "PUT /api/v1/settings/zoom": {
3858
+ "status": "covered",
3859
+ "tools": [
3860
+ "conduyt_zoom_connect"
3861
+ ]
3862
+ },
3863
+ "DELETE /api/v1/settings/zoom": {
3864
+ "status": "covered",
3865
+ "tools": [
3866
+ "conduyt_zoom_connect"
3867
+ ]
3868
+ },
3869
+ "POST /api/v1/settings/zoom/test": {
3870
+ "status": "covered",
3871
+ "tools": [
3872
+ "conduyt_zoom_test"
3873
+ ]
3874
+ },
3843
3875
  "GET /api/v1/smart-lists": {
3844
3876
  "status": "covered",
3845
3877
  "tools": [