@elitedcs/ghl-mcp 3.34.8 → 3.34.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.34.9 — Calendar availability: create_calendar / update_calendar now set `openHours`
4
+
5
+ Closes the on-camera "assign availability hours" gap. `create_calendar` and
6
+ `update_calendar` previously exposed no availability parameter, so every calendar
7
+ was created with `openHours: {}` and a caller could not set business hours through
8
+ the MCP — the calendar only offered GHL's default window.
9
+
10
+ - **New `openHours` (and `availabilityType`) params on `create_calendar` and
11
+ `update_calendar`.** `openHours` is an array of `{daysOfTheWeek:[0-6], hours:
12
+ [{openHour,openMinute,closeHour,closeMinute}]}` (0=Sun … 6=Sat, 24-hour clock).
13
+ When `openHours` is supplied the builder also sends `availabilityType: 0`
14
+ (standard weekly hours) — the exact payload GHL honors — unless the caller
15
+ overrides it.
16
+ - **Multi-day blocks are auto-expanded to one block per day.** GHL's CREATE
17
+ endpoint 422s (`openHours.0.must be a valid day of week`) on a block listing more
18
+ than one weekday. Single-day-per-block is GHL's own canonical form (what it reads
19
+ back) and is accepted by both create and update. The builder accepts the natural
20
+ compact form (`daysOfTheWeek:[1,2,3,4,5]`) and expands it to per-day blocks. Logic
21
+ extracted to `buildCreateCalendarBody` / `buildUpdateCalendarBody` /
22
+ `expandOpenHours`, with tests.
23
+
24
+ **Corrects the 3.34.8 note.** The earlier guess — that round-robin availability
25
+ comes only from the user's working hours and not a calendar `openHours` field — was
26
+ wrong. Pinned live on a real sub-account, the full model is:
27
+
28
+ - `openHours` **does** drive availability. Setting it changes `/free-slots`
29
+ immediately (a Wed-only 13:00–15:00 window collapsed slots to Wednesdays
30
+ 13:00–14:30; a Mon–Fri 9–6 window produced exactly 09:00–17:30 each weekday).
31
+ - On **event / simple** calendars `openHours` is the sole source — every configured
32
+ day/time becomes bookable (a Sat 10–1 window booked Saturdays).
33
+ - On **round_robin** "OptimizeForAvailability" calendars the bookable slots are the
34
+ **intersection** of `openHours` and each assigned user's working hours, so a day
35
+ only opens if a team member is also available then (the same Sat 10–1 window
36
+ produced zero slots when the lone user had no Saturday availability). To open a day
37
+ on round_robin, set the user's availability too (UI-gated), or use an event-type
38
+ calendar where `openHours` alone controls. The tool descriptions state this.
39
+
40
+ Write-verified live: created calendars with hours through the builders, read the
41
+ hours back, and confirmed `/free-slots` returned exactly the configured days/times;
42
+ all throwaways deleted. Suite 289 pass / 1 skip.
43
+
3
44
  ## 3.34.8 — create_opportunity + get_funnel_pages no longer 422 on omitted required params
4
45
 
5
46
  Two real defects found during a full end-to-end build test on a live sub-account
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "@elitedcs/ghl-mcp",
34
- version: "3.34.8",
34
+ version: "3.34.9",
35
35
  mcpName: "io.github.drjerryrelth/ghl-command",
36
36
  description: "GoHighLevel MCP Server for Claude. 218 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
37
37
  main: "dist/index.js",
@@ -2727,6 +2727,58 @@ var CalendarTeamMemberSchema = import_zod9.z.object({
2727
2727
  selected: import_zod9.z.boolean().describe("Whether this user is currently active on the calendar (true to enable)."),
2728
2728
  locationConfigurations: import_zod9.z.array(TeamMemberLocationConfigSchema).min(1).describe("At least one meeting-location config. Typical shape: [{kind:'custom', position:0, location:''}].")
2729
2729
  }).strict();
2730
+ var OpenHoursWindowSchema = import_zod9.z.object({
2731
+ openHour: import_zod9.z.number().int().min(0).max(23).describe("Opening hour on a 24-hour clock (0-23)."),
2732
+ openMinute: import_zod9.z.number().int().min(0).max(59).describe("Opening minute (0-59)."),
2733
+ closeHour: import_zod9.z.number().int().min(0).max(23).describe("Closing hour on a 24-hour clock (0-23)."),
2734
+ closeMinute: import_zod9.z.number().int().min(0).max(59).describe("Closing minute (0-59).")
2735
+ }).strict();
2736
+ var OpenHoursBlockSchema = import_zod9.z.object({
2737
+ daysOfTheWeek: import_zod9.z.array(import_zod9.z.number().int().min(0).max(6)).min(1).describe("Weekdays this block applies to. 0=Sunday, 1=Monday, \u2026 6=Saturday."),
2738
+ hours: import_zod9.z.array(OpenHoursWindowSchema).min(1).describe("One or more open time windows that apply to these days.")
2739
+ }).strict();
2740
+ function expandOpenHours(blocks) {
2741
+ const out = [];
2742
+ for (const block of blocks) {
2743
+ for (const day of block.daysOfTheWeek) {
2744
+ out.push({ daysOfTheWeek: [day], hours: block.hours });
2745
+ }
2746
+ }
2747
+ return out;
2748
+ }
2749
+ function applyCalendarFields(body, args) {
2750
+ if (args.description !== void 0) body.description = args.description;
2751
+ if (args.slug !== void 0) body.slug = args.slug;
2752
+ if (args.widgetSlug !== void 0) body.widgetSlug = args.widgetSlug;
2753
+ if (args.calendarType !== void 0) body.calendarType = args.calendarType;
2754
+ if (args.teamMembers !== void 0) body.teamMembers = args.teamMembers;
2755
+ if (args.eventTitle !== void 0) body.eventTitle = args.eventTitle;
2756
+ if (args.eventColor !== void 0) body.eventColor = args.eventColor;
2757
+ if (args.slotDuration !== void 0) body.slotDuration = args.slotDuration;
2758
+ if (args.slotBuffer !== void 0) body.slotBuffer = args.slotBuffer;
2759
+ if (args.slotInterval !== void 0) body.slotInterval = args.slotInterval;
2760
+ if (args.appointmentPerSlot !== void 0) body.appointmentPerSlot = args.appointmentPerSlot;
2761
+ if (args.openHours !== void 0) {
2762
+ body.openHours = expandOpenHours(args.openHours);
2763
+ body.availabilityType = args.availabilityType ?? 0;
2764
+ } else if (args.availabilityType !== void 0) {
2765
+ body.availabilityType = args.availabilityType;
2766
+ }
2767
+ }
2768
+ function buildCreateCalendarBody(args, resolvedLocationId) {
2769
+ const body = {
2770
+ locationId: resolvedLocationId,
2771
+ name: args.name
2772
+ };
2773
+ applyCalendarFields(body, args);
2774
+ return body;
2775
+ }
2776
+ function buildUpdateCalendarBody(args) {
2777
+ const body = {};
2778
+ if (args.name !== void 0) body.name = args.name;
2779
+ applyCalendarFields(body, args);
2780
+ return body;
2781
+ }
2730
2782
  function registerCalendarTools(server2, client) {
2731
2783
  safeTool(
2732
2784
  server2,
@@ -2771,25 +2823,15 @@ function registerCalendarTools(server2, client) {
2771
2823
  slotDuration: import_zod9.z.number().optional().describe("Slot duration in minutes"),
2772
2824
  slotBuffer: import_zod9.z.number().optional().describe("Buffer time between slots in minutes"),
2773
2825
  slotInterval: import_zod9.z.number().optional().describe("Slot interval in minutes"),
2774
- appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot")
2826
+ appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot"),
2827
+ openHours: import_zod9.z.array(OpenHoursBlockSchema).optional().describe(
2828
+ "Weekly business hours that make slots bookable (the 'assign availability hours' step). Each entry maps weekdays (0=Sun \u2026 6=Sat) to one or more open windows on a 24-hour clock; multi-day entries are auto-expanded to GHL's required one-block-per-day form. Example Mon-Fri 9-6 + Sat 10-1: [{daysOfTheWeek:[1,2,3,4,5],hours:[{openHour:9,openMinute:0,closeHour:18,closeMinute:0}]},{daysOfTheWeek:[6],hours:[{openHour:10,openMinute:0,closeHour:13,closeMinute:0}]}]. Behavior (verified live): on event/simple calendars these hours are the sole source of availability. On round_robin calendars the bookable slots are the INTERSECTION of these hours and each assigned user's working hours, so a day only opens if a team member is also available then (e.g. a Saturday window needs a user with Saturday availability). Omit to use GHL's defaults."
2829
+ ),
2830
+ availabilityType: import_zod9.z.number().int().optional().describe("Availability mode. 0 = standard weekly hours (from openHours); auto-set when you pass openHours. Leave unset otherwise.")
2775
2831
  },
2776
- async ({ locationId: locationId2, name, description, slug, widgetSlug, calendarType, teamMembers, eventTitle, eventColor, slotDuration, slotBuffer, slotInterval, appointmentPerSlot }) => {
2777
- const resolvedLocationId = client.resolveLocationId(locationId2);
2778
- const body = {
2779
- locationId: resolvedLocationId,
2780
- name
2781
- };
2782
- if (description !== void 0) body.description = description;
2783
- if (slug !== void 0) body.slug = slug;
2784
- if (widgetSlug !== void 0) body.widgetSlug = widgetSlug;
2785
- if (calendarType !== void 0) body.calendarType = calendarType;
2786
- if (teamMembers !== void 0) body.teamMembers = teamMembers;
2787
- if (eventTitle !== void 0) body.eventTitle = eventTitle;
2788
- if (eventColor !== void 0) body.eventColor = eventColor;
2789
- if (slotDuration !== void 0) body.slotDuration = slotDuration;
2790
- if (slotBuffer !== void 0) body.slotBuffer = slotBuffer;
2791
- if (slotInterval !== void 0) body.slotInterval = slotInterval;
2792
- if (appointmentPerSlot !== void 0) body.appointmentPerSlot = appointmentPerSlot;
2832
+ async (args) => {
2833
+ const resolvedLocationId = client.resolveLocationId(args.locationId);
2834
+ const body = buildCreateCalendarBody(args, resolvedLocationId);
2793
2835
  return await client.post("/calendars/", { body });
2794
2836
  }
2795
2837
  );
@@ -2810,23 +2852,15 @@ function registerCalendarTools(server2, client) {
2810
2852
  slotDuration: import_zod9.z.number().optional().describe("Slot duration in minutes"),
2811
2853
  slotBuffer: import_zod9.z.number().optional().describe("Buffer time between slots in minutes"),
2812
2854
  slotInterval: import_zod9.z.number().optional().describe("Slot interval in minutes"),
2813
- appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot")
2855
+ appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot"),
2856
+ openHours: import_zod9.z.array(OpenHoursBlockSchema).optional().describe(
2857
+ "Weekly business hours that make slots bookable. Each entry maps weekdays (0=Sun \u2026 6=Sat) to one or more open windows on a 24-hour clock; multi-day entries are auto-expanded to GHL's required one-block-per-day form (e.g. [{daysOfTheWeek:[1,2,3,4,5],hours:[{openHour:9,openMinute:0,closeHour:18,closeMinute:0}]}] = Mon-Fri 9am-6pm). Replaces the calendar's existing hours. Behavior (verified live): event/simple calendars use these hours directly; round_robin calendars intersect them with each assigned user's working hours (a day only opens if a team member is available then)."
2858
+ ),
2859
+ availabilityType: import_zod9.z.number().int().optional().describe("Availability mode. 0 = standard weekly hours (from openHours); auto-set when you pass openHours. Leave unset otherwise.")
2814
2860
  },
2815
- async ({ calendarId, name, description, slug, widgetSlug, calendarType, teamMembers, eventTitle, eventColor, slotDuration, slotBuffer, slotInterval, appointmentPerSlot }) => {
2816
- const body = {};
2817
- if (name !== void 0) body.name = name;
2818
- if (description !== void 0) body.description = description;
2819
- if (slug !== void 0) body.slug = slug;
2820
- if (widgetSlug !== void 0) body.widgetSlug = widgetSlug;
2821
- if (calendarType !== void 0) body.calendarType = calendarType;
2822
- if (teamMembers !== void 0) body.teamMembers = teamMembers;
2823
- if (eventTitle !== void 0) body.eventTitle = eventTitle;
2824
- if (eventColor !== void 0) body.eventColor = eventColor;
2825
- if (slotDuration !== void 0) body.slotDuration = slotDuration;
2826
- if (slotBuffer !== void 0) body.slotBuffer = slotBuffer;
2827
- if (slotInterval !== void 0) body.slotInterval = slotInterval;
2828
- if (appointmentPerSlot !== void 0) body.appointmentPerSlot = appointmentPerSlot;
2829
- return await client.put(`/calendars/${calendarId}`, { body });
2861
+ async (args) => {
2862
+ const body = buildUpdateCalendarBody(args);
2863
+ return await client.put(`/calendars/${args.calendarId}`, { body });
2830
2864
  }
2831
2865
  );
2832
2866
  safeTool(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.34.8",
3
+ "version": "3.34.9",
4
4
  "mcpName": "io.github.drjerryrelth/ghl-command",
5
5
  "description": "GoHighLevel MCP Server for Claude. 218 tools — full CRM, automation, marketing control, account-wide workflow audit, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
6
6
  "main": "dist/index.js",