@elitedcs/ghl-mcp 3.34.8 → 3.34.10
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 +66 -0
- package/dist/index.js +173 -46
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.34.10 — `get_free_slots` accepts ISO dates; `update_form` name fallback for fresh forms
|
|
4
|
+
|
|
5
|
+
Two small fixes that close rough edges found while live-verifying v3.34.9 through
|
|
6
|
+
the MCP tools. Neither changes a proven capability; both have trivial workarounds.
|
|
7
|
+
|
|
8
|
+
- **`get_free_slots` now accepts ISO dates (its documented input) — not just epoch
|
|
9
|
+
milliseconds.** GHL's `GET /calendars/{id}/free-slots` requires `startDate` /
|
|
10
|
+
`endDate` as epoch **milliseconds** and 422s (`startDate must be a number…`) on an
|
|
11
|
+
ISO date, yet the tool's own schema documented `YYYY-MM-DD` and forwarded it
|
|
12
|
+
unconverted — so the documented input always failed. The tool now accepts a bare
|
|
13
|
+
`YYYY-MM-DD` (taken as start-of-day for `startDate`, end-of-day for `endDate`, in
|
|
14
|
+
the supplied `timezone`, default UTC), a full ISO datetime (`Date.parse`), or an
|
|
15
|
+
already-numeric epoch-millis value (passed through, back-compat) and converts to
|
|
16
|
+
the epoch ms GHL needs. Logic extracted to `buildFreeSlotsParams` / `toEpochMillis`
|
|
17
|
+
with tests (incl. the live Phoenix anchor `2026-06-15 → 1781506800000`).
|
|
18
|
+
- **`update_form` no longer throws on the natural `create_form` → `update_form`
|
|
19
|
+
sequence.** When `name` is omitted, `update_form` reads the current name to
|
|
20
|
+
preserve it. A form freshly created by `create_form` has no `name` in its builder
|
|
21
|
+
doc until its first save, so that read returned none and the call threw
|
|
22
|
+
(`Could not resolve current form name`). It now falls back to the public forms list
|
|
23
|
+
(which carries the name) and only errors — with actionable guidance to pass `name`
|
|
24
|
+
— if both sources come up empty. The form-builder tools now also receive the public
|
|
25
|
+
`GHLClient` for this lookup (it already had Firebase auth for the builder routes).
|
|
26
|
+
Helpers `pickFormName` / `findFormNameInList` extracted, with tests.
|
|
27
|
+
|
|
28
|
+
## 3.34.9 — Calendar availability: create_calendar / update_calendar now set `openHours`
|
|
29
|
+
|
|
30
|
+
Closes the on-camera "assign availability hours" gap. `create_calendar` and
|
|
31
|
+
`update_calendar` previously exposed no availability parameter, so every calendar
|
|
32
|
+
was created with `openHours: {}` and a caller could not set business hours through
|
|
33
|
+
the MCP — the calendar only offered GHL's default window.
|
|
34
|
+
|
|
35
|
+
- **New `openHours` (and `availabilityType`) params on `create_calendar` and
|
|
36
|
+
`update_calendar`.** `openHours` is an array of `{daysOfTheWeek:[0-6], hours:
|
|
37
|
+
[{openHour,openMinute,closeHour,closeMinute}]}` (0=Sun … 6=Sat, 24-hour clock).
|
|
38
|
+
When `openHours` is supplied the builder also sends `availabilityType: 0`
|
|
39
|
+
(standard weekly hours) — the exact payload GHL honors — unless the caller
|
|
40
|
+
overrides it.
|
|
41
|
+
- **Multi-day blocks are auto-expanded to one block per day.** GHL's CREATE
|
|
42
|
+
endpoint 422s (`openHours.0.must be a valid day of week`) on a block listing more
|
|
43
|
+
than one weekday. Single-day-per-block is GHL's own canonical form (what it reads
|
|
44
|
+
back) and is accepted by both create and update. The builder accepts the natural
|
|
45
|
+
compact form (`daysOfTheWeek:[1,2,3,4,5]`) and expands it to per-day blocks. Logic
|
|
46
|
+
extracted to `buildCreateCalendarBody` / `buildUpdateCalendarBody` /
|
|
47
|
+
`expandOpenHours`, with tests.
|
|
48
|
+
|
|
49
|
+
**Corrects the 3.34.8 note.** The earlier guess — that round-robin availability
|
|
50
|
+
comes only from the user's working hours and not a calendar `openHours` field — was
|
|
51
|
+
wrong. Pinned live on a real sub-account, the full model is:
|
|
52
|
+
|
|
53
|
+
- `openHours` **does** drive availability. Setting it changes `/free-slots`
|
|
54
|
+
immediately (a Wed-only 13:00–15:00 window collapsed slots to Wednesdays
|
|
55
|
+
13:00–14:30; a Mon–Fri 9–6 window produced exactly 09:00–17:30 each weekday).
|
|
56
|
+
- On **event / simple** calendars `openHours` is the sole source — every configured
|
|
57
|
+
day/time becomes bookable (a Sat 10–1 window booked Saturdays).
|
|
58
|
+
- On **round_robin** "OptimizeForAvailability" calendars the bookable slots are the
|
|
59
|
+
**intersection** of `openHours` and each assigned user's working hours, so a day
|
|
60
|
+
only opens if a team member is also available then (the same Sat 10–1 window
|
|
61
|
+
produced zero slots when the lone user had no Saturday availability). To open a day
|
|
62
|
+
on round_robin, set the user's availability too (UI-gated), or use an event-type
|
|
63
|
+
calendar where `openHours` alone controls. The tool descriptions state this.
|
|
64
|
+
|
|
65
|
+
Write-verified live: created calendars with hours through the builders, read the
|
|
66
|
+
hours back, and confirmed `/free-slots` returned exactly the configured days/times;
|
|
67
|
+
all throwaways deleted. Suite 289 pass / 1 skip.
|
|
68
|
+
|
|
3
69
|
## 3.34.8 — create_opportunity + get_funnel_pages no longer 422 on omitted required params
|
|
4
70
|
|
|
5
71
|
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.
|
|
34
|
+
version: "3.34.10",
|
|
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,113 @@ 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
|
+
}
|
|
2782
|
+
function tzOffsetMillis(utcMillis, timeZone) {
|
|
2783
|
+
const dtf = new Intl.DateTimeFormat("en-US", {
|
|
2784
|
+
timeZone,
|
|
2785
|
+
hourCycle: "h23",
|
|
2786
|
+
year: "numeric",
|
|
2787
|
+
month: "2-digit",
|
|
2788
|
+
day: "2-digit",
|
|
2789
|
+
hour: "2-digit",
|
|
2790
|
+
minute: "2-digit",
|
|
2791
|
+
second: "2-digit"
|
|
2792
|
+
});
|
|
2793
|
+
const parts = dtf.formatToParts(new Date(utcMillis));
|
|
2794
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
2795
|
+
const asUTC = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour"), get("minute"), get("second"));
|
|
2796
|
+
return asUTC - utcMillis;
|
|
2797
|
+
}
|
|
2798
|
+
function zonedStartOfDayMillis(year, month, day, timeZone) {
|
|
2799
|
+
const utcGuess = Date.UTC(year, month - 1, day, 0, 0, 0, 0);
|
|
2800
|
+
if (!timeZone) return utcGuess;
|
|
2801
|
+
return utcGuess - tzOffsetMillis(utcGuess, timeZone);
|
|
2802
|
+
}
|
|
2803
|
+
function zonedDayBoundaryMillis(year, month, day, endOfDay, timeZone) {
|
|
2804
|
+
if (endOfDay) return zonedStartOfDayMillis(year, month, day + 1, timeZone) - 1;
|
|
2805
|
+
return zonedStartOfDayMillis(year, month, day, timeZone);
|
|
2806
|
+
}
|
|
2807
|
+
function toEpochMillis(value, opts = {}) {
|
|
2808
|
+
const trimmed = value.trim();
|
|
2809
|
+
if (/^\d+$/.test(trimmed)) {
|
|
2810
|
+
if (trimmed.length === 13) return trimmed;
|
|
2811
|
+
throw new Error(`Ambiguous numeric date "${value}" for free-slots \u2014 pass epoch MILLISECONDS (13 digits), a YYYY-MM-DD date, or a full ISO datetime.`);
|
|
2812
|
+
}
|
|
2813
|
+
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed);
|
|
2814
|
+
if (dateOnly) {
|
|
2815
|
+
const millis = zonedDayBoundaryMillis(
|
|
2816
|
+
Number(dateOnly[1]),
|
|
2817
|
+
Number(dateOnly[2]),
|
|
2818
|
+
Number(dateOnly[3]),
|
|
2819
|
+
Boolean(opts.endOfDay),
|
|
2820
|
+
opts.timeZone
|
|
2821
|
+
);
|
|
2822
|
+
return String(millis);
|
|
2823
|
+
}
|
|
2824
|
+
const parsed = Date.parse(trimmed);
|
|
2825
|
+
if (!Number.isNaN(parsed)) return String(parsed);
|
|
2826
|
+
throw new Error(`Invalid date "${value}" for free-slots \u2014 use YYYY-MM-DD, a full ISO datetime, or epoch milliseconds.`);
|
|
2827
|
+
}
|
|
2828
|
+
function buildFreeSlotsParams(args) {
|
|
2829
|
+
const params = {
|
|
2830
|
+
startDate: toEpochMillis(args.startDate, { timeZone: args.timezone }),
|
|
2831
|
+
endDate: toEpochMillis(args.endDate, { endOfDay: true, timeZone: args.timezone })
|
|
2832
|
+
};
|
|
2833
|
+
if (args.timezone !== void 0) params.timezone = args.timezone;
|
|
2834
|
+
if (args.userId !== void 0) params.userId = args.userId;
|
|
2835
|
+
return params;
|
|
2836
|
+
}
|
|
2730
2837
|
function registerCalendarTools(server2, client) {
|
|
2731
2838
|
safeTool(
|
|
2732
2839
|
server2,
|
|
@@ -2771,25 +2878,15 @@ function registerCalendarTools(server2, client) {
|
|
|
2771
2878
|
slotDuration: import_zod9.z.number().optional().describe("Slot duration in minutes"),
|
|
2772
2879
|
slotBuffer: import_zod9.z.number().optional().describe("Buffer time between slots in minutes"),
|
|
2773
2880
|
slotInterval: import_zod9.z.number().optional().describe("Slot interval in minutes"),
|
|
2774
|
-
appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot")
|
|
2881
|
+
appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot"),
|
|
2882
|
+
openHours: import_zod9.z.array(OpenHoursBlockSchema).optional().describe(
|
|
2883
|
+
"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."
|
|
2884
|
+
),
|
|
2885
|
+
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
2886
|
},
|
|
2776
|
-
async (
|
|
2777
|
-
const resolvedLocationId = client.resolveLocationId(
|
|
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;
|
|
2887
|
+
async (args) => {
|
|
2888
|
+
const resolvedLocationId = client.resolveLocationId(args.locationId);
|
|
2889
|
+
const body = buildCreateCalendarBody(args, resolvedLocationId);
|
|
2793
2890
|
return await client.post("/calendars/", { body });
|
|
2794
2891
|
}
|
|
2795
2892
|
);
|
|
@@ -2810,23 +2907,15 @@ function registerCalendarTools(server2, client) {
|
|
|
2810
2907
|
slotDuration: import_zod9.z.number().optional().describe("Slot duration in minutes"),
|
|
2811
2908
|
slotBuffer: import_zod9.z.number().optional().describe("Buffer time between slots in minutes"),
|
|
2812
2909
|
slotInterval: import_zod9.z.number().optional().describe("Slot interval in minutes"),
|
|
2813
|
-
appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot")
|
|
2910
|
+
appointmentPerSlot: import_zod9.z.number().optional().describe("Max appointments per slot"),
|
|
2911
|
+
openHours: import_zod9.z.array(OpenHoursBlockSchema).optional().describe(
|
|
2912
|
+
"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)."
|
|
2913
|
+
),
|
|
2914
|
+
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
2915
|
},
|
|
2815
|
-
async (
|
|
2816
|
-
const body =
|
|
2817
|
-
|
|
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 });
|
|
2916
|
+
async (args) => {
|
|
2917
|
+
const body = buildUpdateCalendarBody(args);
|
|
2918
|
+
return await client.put(`/calendars/${args.calendarId}`, { body });
|
|
2830
2919
|
}
|
|
2831
2920
|
);
|
|
2832
2921
|
safeTool(
|
|
@@ -2847,15 +2936,13 @@ function registerCalendarTools(server2, client) {
|
|
|
2847
2936
|
"Get available free slots for a calendar",
|
|
2848
2937
|
{
|
|
2849
2938
|
calendarId: import_zod9.z.string().describe("The calendar ID"),
|
|
2850
|
-
startDate: import_zod9.z.string().describe("Start
|
|
2851
|
-
endDate: import_zod9.z.string().describe("End
|
|
2939
|
+
startDate: import_zod9.z.string().describe("Start of the window. Accepts a date (YYYY-MM-DD), a full ISO datetime, or epoch milliseconds. A bare date is taken as start-of-day in `timezone` (UTC if unset). GHL's API needs epoch ms; the tool converts for you."),
|
|
2940
|
+
endDate: import_zod9.z.string().describe("End of the window. Accepts a date (YYYY-MM-DD), a full ISO datetime, or epoch milliseconds. A bare date is taken as end-of-day in `timezone` (UTC if unset)."),
|
|
2852
2941
|
timezone: import_zod9.z.string().optional().describe("Timezone (e.g. America/New_York)"),
|
|
2853
2942
|
userId: import_zod9.z.string().optional().describe("Filter by user ID")
|
|
2854
2943
|
},
|
|
2855
2944
|
async ({ calendarId, startDate, endDate, timezone, userId }) => {
|
|
2856
|
-
const params = { startDate, endDate };
|
|
2857
|
-
if (timezone !== void 0) params.timezone = timezone;
|
|
2858
|
-
if (userId !== void 0) params.userId = userId;
|
|
2945
|
+
const params = buildFreeSlotsParams({ startDate, endDate, timezone, userId });
|
|
2859
2946
|
return await client.get(`/calendars/${calendarId}/free-slots`, {
|
|
2860
2947
|
params
|
|
2861
2948
|
});
|
|
@@ -5973,13 +6060,34 @@ ${text2}`);
|
|
|
5973
6060
|
|
|
5974
6061
|
// src/tools/form-builder.ts
|
|
5975
6062
|
var import_zod35 = require("zod");
|
|
6063
|
+
function pickFormName(builderForm) {
|
|
6064
|
+
if (builderForm && typeof builderForm === "object" && "form" in builderForm) {
|
|
6065
|
+
const name = builderForm.form?.name;
|
|
6066
|
+
if (typeof name === "string" && name.length > 0) return name;
|
|
6067
|
+
}
|
|
6068
|
+
return void 0;
|
|
6069
|
+
}
|
|
6070
|
+
function findFormNameInList(list, formId) {
|
|
6071
|
+
const forms = list?.forms;
|
|
6072
|
+
if (Array.isArray(forms)) {
|
|
6073
|
+
for (const f of forms) {
|
|
6074
|
+
if (f && typeof f === "object") {
|
|
6075
|
+
const entry = f;
|
|
6076
|
+
if ((entry.id === formId || entry._id === formId) && typeof entry.name === "string" && entry.name.length > 0) {
|
|
6077
|
+
return entry.name;
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
return void 0;
|
|
6083
|
+
}
|
|
5976
6084
|
function buildUpdateFormPath(formId, locationId2) {
|
|
5977
6085
|
return `/${formId}?locationId=${locationId2}`;
|
|
5978
6086
|
}
|
|
5979
6087
|
function buildUpdateFormBody(name, formData) {
|
|
5980
6088
|
return { name, formData };
|
|
5981
6089
|
}
|
|
5982
|
-
function registerFormBuilderTools(server2, builderClient) {
|
|
6090
|
+
function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
5983
6091
|
const client = builderClient;
|
|
5984
6092
|
if (!client) return;
|
|
5985
6093
|
async function formRequest(method, path7, body) {
|
|
@@ -6033,11 +6141,28 @@ ${text2}`);
|
|
|
6033
6141
|
let resolvedName = name;
|
|
6034
6142
|
if (resolvedName === void 0) {
|
|
6035
6143
|
const current = await formRequest("GET", `/${formId}?locationId=${client.locationId}`);
|
|
6036
|
-
|
|
6037
|
-
if (
|
|
6038
|
-
|
|
6144
|
+
resolvedName = pickFormName(current);
|
|
6145
|
+
if (resolvedName === void 0 && publicClient) {
|
|
6146
|
+
try {
|
|
6147
|
+
const pageSize = 100;
|
|
6148
|
+
const maxForms = 5e3;
|
|
6149
|
+
for (let skip = 0; skip < maxForms; skip += pageSize) {
|
|
6150
|
+
const list = await publicClient.get("/forms/", {
|
|
6151
|
+
params: { locationId: client.locationId, limit: pageSize, skip }
|
|
6152
|
+
});
|
|
6153
|
+
resolvedName = findFormNameInList(list, formId);
|
|
6154
|
+
if (resolvedName !== void 0) break;
|
|
6155
|
+
const forms = list?.forms;
|
|
6156
|
+
if (!Array.isArray(forms) || forms.length < pageSize) break;
|
|
6157
|
+
}
|
|
6158
|
+
} catch {
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
if (resolvedName === void 0) {
|
|
6162
|
+
throw new Error(
|
|
6163
|
+
`Could not resolve the current name for form ${formId}. Pass the \`name\` argument explicitly \u2014 a form created via create_form has no builder-doc name until its first save.`
|
|
6164
|
+
);
|
|
6039
6165
|
}
|
|
6040
|
-
resolvedName = currentName;
|
|
6041
6166
|
}
|
|
6042
6167
|
const result = await formRequest(
|
|
6043
6168
|
"POST",
|
|
@@ -9511,7 +9636,6 @@ var publicApiTools = [
|
|
|
9511
9636
|
var internalApiTools = [
|
|
9512
9637
|
[registerWorkflowBuilderTools, "workflow-builder"],
|
|
9513
9638
|
[registerFunnelBuilderTools, "funnel-builder"],
|
|
9514
|
-
[registerFormBuilderTools, "form-builder"],
|
|
9515
9639
|
[registerPipelineBuilderTools, "pipeline-builder"],
|
|
9516
9640
|
[registerWorkflowClonerTools, "workflow-cloner"],
|
|
9517
9641
|
[registerSmartListTools, "smart-lists"],
|
|
@@ -9524,9 +9648,11 @@ var VALIDATORS_MODULE = "validators";
|
|
|
9524
9648
|
var DIAGNOSTICS_MODULE = "diagnostics";
|
|
9525
9649
|
var LOCATION_SWITCHER_MODULE = "location-switcher";
|
|
9526
9650
|
var SNAPSHOTS_MODULE = "snapshots";
|
|
9651
|
+
var FORM_BUILDER_MODULE = "form-builder";
|
|
9527
9652
|
var KNOWN_MODULES = /* @__PURE__ */ new Set([
|
|
9528
9653
|
...publicApiTools.map(([, label]) => label),
|
|
9529
9654
|
...internalApiTools.map(([, label]) => label),
|
|
9655
|
+
FORM_BUILDER_MODULE,
|
|
9530
9656
|
VALIDATORS_MODULE,
|
|
9531
9657
|
DIAGNOSTICS_MODULE,
|
|
9532
9658
|
LOCATION_SWITCHER_MODULE,
|
|
@@ -9544,6 +9670,7 @@ function registerAllTools(server2, client, registry2, mcpVersion, env = process.
|
|
|
9544
9670
|
for (const [register, moduleName] of internalApiTools) {
|
|
9545
9671
|
register(wrap(moduleName), builderClient);
|
|
9546
9672
|
}
|
|
9673
|
+
registerFormBuilderTools(wrap(FORM_BUILDER_MODULE), builderClient, client);
|
|
9547
9674
|
registerValidatorTools(wrap(VALIDATORS_MODULE), client, builderClient);
|
|
9548
9675
|
registerDiagnosticTools(
|
|
9549
9676
|
wrap(DIAGNOSTICS_MODULE),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.34.
|
|
3
|
+
"version": "3.34.10",
|
|
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",
|