@elitedcs/ghl-mcp 3.72.2 → 3.72.3

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,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.72.3 — the waits in a built account can actually go live
4
+
5
+ Blueprint builds a client's account and then asks whether to publish the workflows or
6
+ leave them as drafts for review. Say publish, and the ones with a wait of a day or more
7
+ were refused by GoHighLevel and stayed as drafts. The build still reported them as
8
+ created, so the account looked finished when the follow-up sequences were not running.
9
+
10
+ The cause was one word. GoHighLevel takes `minutes` and `hour`, but for days it wants
11
+ `days`, and we were sending `day`. It is not a rule you can guess: a workflow that is
12
+ nothing but a wait accepts the singular quite happily, which is why this went unnoticed.
13
+ It only fails on the shape a real nurture has, a message followed by a wait, which is
14
+ every follow-up sequence the presets build. **123 of the waits in the shipped presets
15
+ were affected.**
16
+
17
+ Workflows already in an account are untouched and keep working; this was only ever about
18
+ newly built ones.
19
+
20
+ **The second half is the reporting.** A workflow that could not be published was still
21
+ counted as built, and the summary read the same as a run where nobody asked to publish at
22
+ all: it suggested turning on publishing, to someone who had just turned it on. The real
23
+ reason sat further down the report where nobody looks first. A build now says plainly
24
+ which workflows could not be published, what GoHighLevel said about each, that they are
25
+ sitting as drafts and not running, and that repeating the build will not help.
26
+
3
27
  ## 3.72.2 — a form you read back can be saved again
4
28
 
5
29
  A subscriber tried to edit one of her forms the documented way: read it with
package/dist/index.js CHANGED
@@ -18523,7 +18523,7 @@ var init_executor = __esm({
18523
18523
  WAIT_UNIT_MAP = {
18524
18524
  minutes: "minutes",
18525
18525
  hours: "hour",
18526
- days: "day"
18526
+ days: "days"
18527
18527
  };
18528
18528
  PENDING = (ref) => `__PENDING__:${ref}`;
18529
18529
  isPending = (v) => v.startsWith("__PENDING__:");
@@ -21493,6 +21493,36 @@ var init_plan_form = __esm({
21493
21493
  }
21494
21494
  });
21495
21495
 
21496
+ // src/intake-to-build/publish-report.ts
21497
+ function publishNote(r) {
21498
+ if (r.publishFailed.length) {
21499
+ const lead = r.published.length ? `Auto-published ${r.published.length} workflow(s) live, but ` : "";
21500
+ const detail = r.publishFailed.map((p) => `${p.name} (${p.error})`).join("; ");
21501
+ return `${lead}${r.publishFailed.length} workflow(s) were BUILT BUT COULD NOT BE PUBLISHED. They exist in GHL as DRAFT and are NOT running: ${detail}. You already opted in, so re-running with publishWorkflows will not help. If GHL rejected the workflow's contents, publishing by hand will hit the SAME error until the cause is fixed. Read the error above, fix it, then publish (see nextManualSteps).${r.published.length ? " Any gated workflow stays DRAFT until its handoff is met." : ""}`;
21502
+ }
21503
+ if (r.published.length) {
21504
+ return `Auto-published ${r.published.length} workflow(s) live (you opted in). Any gated workflow stays DRAFT until its handoff is met.`;
21505
+ }
21506
+ if (r.anyWorkflowCreated) {
21507
+ return "Workflows were built as DRAFT (the safe default). Review them in GHL and publish, or re-run with publishWorkflows:true to auto-publish ungated ones.";
21508
+ }
21509
+ return "No workflows were built in this run (none in the plan, or all already existed and were left untouched).";
21510
+ }
21511
+ function publishSummaryClause(r) {
21512
+ if (r.publishFailed.length) {
21513
+ const also = r.published.length ? ` (${r.published.length} did publish)` : "";
21514
+ return ` ${r.publishFailed.length} workflow(s) BUILT BUT NOT PUBLISHED${also}. They are DRAFT and not running, see publishNote.`;
21515
+ }
21516
+ if (r.published.length) return ` Published ${r.published.length} workflow(s) live.`;
21517
+ if (r.anyWorkflowCreated) return " Workflows are DRAFT (opt in with publishWorkflows to auto-publish).";
21518
+ return "";
21519
+ }
21520
+ var init_publish_report = __esm({
21521
+ "src/intake-to-build/publish-report.ts"() {
21522
+ "use strict";
21523
+ }
21524
+ });
21525
+
21496
21526
  // src/intake-to-build/customization.ts
21497
21527
  function industryPack(slug3) {
21498
21528
  if (!slug3) return void 0;
@@ -23424,6 +23454,7 @@ async function executeBackbone(plan, deps, opts = {}) {
23424
23454
  const built = [];
23425
23455
  const manual = [];
23426
23456
  const published = [];
23457
+ const publishFailed = [];
23427
23458
  const staff = [];
23428
23459
  const templates = { emails: [], sms: [] };
23429
23460
  let templatesUnverified = 0;
@@ -23447,6 +23478,7 @@ async function executeBackbone(plan, deps, opts = {}) {
23447
23478
  built,
23448
23479
  manual,
23449
23480
  published,
23481
+ publishFailed,
23450
23482
  halted: { atRef, type, reason },
23451
23483
  deferred,
23452
23484
  staff,
@@ -24124,6 +24156,7 @@ async function executeBackbone(plan, deps, opts = {}) {
24124
24156
  await deps.publishWorkflow(wfId);
24125
24157
  published.push(wf.ref);
24126
24158
  } catch (e) {
24159
+ publishFailed.push({ ref: wf.ref, name: wf.name, error: msg2(e) });
24127
24160
  manual.push({ ref: wf.ref, type: "workflow-publish", name: wf.name, reason: `[${wf.name}] built successfully but auto-publish failed (${msg2(e)}) \u2014 publish it manually in GHL.` });
24128
24161
  }
24129
24162
  }
@@ -24178,7 +24211,7 @@ async function executeBackbone(plan, deps, opts = {}) {
24178
24211
  }
24179
24212
  }
24180
24213
  }
24181
- return { ok: true, idMap, built, manual, published, deferred, staff, templates, templatesUnverified, templatesFormatNote, calendarPending, refreshed, missingSteps, recipients };
24214
+ return { ok: true, idMap, built, manual, published, publishFailed, deferred, staff, templates, templatesUnverified, templatesFormatNote, calendarPending, refreshed, missingSteps, recipients };
24182
24215
  }
24183
24216
  async function buildSimple(objects, type, list2, create, nameOf, pollForNew, idMap, built) {
24184
24217
  if (objects.length === 0) return null;
@@ -25190,13 +25223,18 @@ function registerIntakeToBuildTools(server2, client, builderClient, registry2) {
25190
25223
  templatesFormatNote: exec.templatesFormatNote,
25191
25224
  templatesNote: exec.templates.emails.length + exec.templates.sms.length ? `The plan's email and SMS copy was also saved as account-level templates (Marketing \u2192 Emails \u2192 Templates; Conversations \u2192 Templates) \u2014 the reusable version your team edits. Templates are created in the editor's own format (vibe-editor) so they open and edit in Marketing \u2192 Emails \u2192 Templates. Workflow steps carry the same copy inline; editing a template does not change a step already built.${exec.templatesUnverified ? ` ${exec.templatesUnverified} email template(s) were saved but their body was NOT seen in GHL's preview \u2014 see templates.emails[].warning; do not report them as written.` : exec.templates.emails.some((t) => t.status === "created") ? ` Every email template created this run had its body verified in GHL's preview.` : ""}` : void 0,
25192
25225
  published: exec.published,
25193
- publishNote: exec.published.length ? `Auto-published ${exec.published.length} workflow(s) live (you opted in). Any gated workflow stays DRAFT until its handoff is met.` : exec.built.some((b) => b.type === "workflow" && b.status === "created") ? "Workflows were built as DRAFT (the safe default). Review them in GHL and publish, or re-run with publishWorkflows:true to auto-publish ungated ones." : "No workflows were built in this run (none in the plan, or all already existed and were left untouched).",
25226
+ publishFailed: exec.publishFailed,
25227
+ publishNote: publishNote({
25228
+ published: exec.published,
25229
+ publishFailed: exec.publishFailed,
25230
+ anyWorkflowCreated: exec.built.some((b) => b.type === "workflow" && b.status === "created")
25231
+ }),
25194
25232
  externalWiring,
25195
25233
  externalWiringNote: externalWiring ? "For each target:\"external\" funnel: plug these into your self-hosted site form + lead bridge (templates/external-funnel/). custom formFields carry the VERIFIED GHL field id your form's `custom` object must send (never name-guess keys); add a triggerTag to start the speed-to-lead workflow; bookingUrl is the GHL calendar widget for the CTA. Anything in `unresolved` wasn't built yet \u2014 re-run after it is." : void 0,
25196
25234
  deferred: exec.deferred,
25197
25235
  deferredNote: `execute builds the WHOLE plan live: staff users (plan.users, via the agency key \u2014 bound by email when they already exist), CRM backbone (pipelines, custom fields, tags, custom values), calendars (with the plan's named team members), forms, funnels (GHL-built = funnel + named steps), account-level email/SMS templates (the plan's reusable copy), and workflows (DRAFT, with all steps incl. opportunity create/move; notification / task steps are NEVER dropped \u2014 with no staff member yet they ship as placeholders flagged staff_pending). Remaining manual steps are surfaced per item: pending staff (no agency key), staff-requiring calendars whose team members are pending (workflows that point at such a calendar are STILL built \u2014 trigger unpinned, flagged calendar_pending \u2014 never a halt), GHL funnel page content/design, EXTERNAL funnels (target:"external" \u2014 the user builds + hosts the site themselves and wires it back; not built here), workflow triggers other than contact_tag, and publishing the DRAFT workflows.`,
25198
25236
  nextManualSteps: [...execManualLines, ...manualLines, ...handoffLines],
25199
- summary: exec.ok ? `Built ${exec.built.filter((b) => b.status === "created").length} new object(s), bound ${exec.built.filter((b) => b.status === "existing" || b.status === "refreshed").length} existing${exec.refreshed.length ? ` (${exec.refreshed.length} workflow(s) refreshed in place \u2014 see refreshed[])` : ""}.${exec.staff.length ? ` Staff: ${exec.staff.filter((s) => s.status === "created").length} created, ${exec.staff.filter((s) => s.status === "existing").length} already in the account, ${exec.staff.filter((s) => s.status === "pending").length} pending.` : ""}${exec.templates.emails.length + exec.templates.sms.length ? ` Templates: ${exec.templates.emails.length} email (${exec.templates.emails.filter((t) => t.status === "created" && !t.warning).length} created with the body verified in the preview${exec.templatesUnverified ? `, ${exec.templatesUnverified} created but NOT verified \u2014 see templates.emails[].warning` : ""}${exec.templates.emails.filter((t) => t.status === "existing").length ? `, ${exec.templates.emails.filter((t) => t.status === "existing").length} already existed` : ""}${exec.templates.emails.filter((t) => t.status === "failed").length ? `, ${exec.templates.emails.filter((t) => t.status === "failed").length} failed` : ""}), ${exec.templates.sms.length} SMS.` : ""}${exec.published.length ? ` Published ${exec.published.length} workflow(s) live.` : exec.built.some((b) => b.type === "workflow" && b.status === "created") ? " Workflows are DRAFT (opt in with publishWorkflows to auto-publish)." : ""}${exec.manual.some((m) => m.type === "calendar") || exec.calendarPending.length ? ` Calendars: ${exec.manual.filter((m) => m.type === "calendar").length} deferred to manual; ${new Set(exec.calendarPending.filter((c) => c.status === "pending").map((c) => c.workflowRef)).size} workflow(s) built with the calendar unpinned${exec.calendarPending.some((c) => c.status === "calendar_ready") ? `, ${new Set(exec.calendarPending.filter((c) => c.status === "calendar_ready").map((c) => c.workflowRef)).size} ready to pin` : ""}.` : ""}${exec.manual.some((m) => m.type === "calendar_team") ? ` ${exec.manual.filter((m) => m.type === "calendar_team").length} calendar(s) were built WITHOUT the people the plan named \u2014 see nextManualSteps; do not report those calendars as staffed.` : ""}${exec.missingSteps.length ? ` NOT COMPLETE: ${exec.missingSteps.reduce((n, w) => n + w.steps.length, 0)} step(s) the plan asks for are still missing from ${exec.missingSteps.length} workflow(s) that already existed \u2014 add them by hand (see missingSteps).` : ""}${exec.manual.length ? ` ${exec.manual.length} item(s) need a manual step (see nextManualSteps).` : ""}${exec.deferred.length ? " Deferred: " + exec.deferred.map((d) => `${d.count} ${d.section}`).join(", ") + " (manual)." : ""}` : `HALTED at ${exec.halted?.atRef} (${exec.halted?.reason}). ${exec.built.length} object(s) were created before the halt \u2014 see idMap to resume or clean up. NEVER-CLOBBER means a re-run will bind those, not duplicate them.`
25237
+ summary: exec.ok ? `Built ${exec.built.filter((b) => b.status === "created").length} new object(s), bound ${exec.built.filter((b) => b.status === "existing" || b.status === "refreshed").length} existing${exec.refreshed.length ? ` (${exec.refreshed.length} workflow(s) refreshed in place \u2014 see refreshed[])` : ""}.${exec.staff.length ? ` Staff: ${exec.staff.filter((s) => s.status === "created").length} created, ${exec.staff.filter((s) => s.status === "existing").length} already in the account, ${exec.staff.filter((s) => s.status === "pending").length} pending.` : ""}${exec.templates.emails.length + exec.templates.sms.length ? ` Templates: ${exec.templates.emails.length} email (${exec.templates.emails.filter((t) => t.status === "created" && !t.warning).length} created with the body verified in the preview${exec.templatesUnverified ? `, ${exec.templatesUnverified} created but NOT verified \u2014 see templates.emails[].warning` : ""}${exec.templates.emails.filter((t) => t.status === "existing").length ? `, ${exec.templates.emails.filter((t) => t.status === "existing").length} already existed` : ""}${exec.templates.emails.filter((t) => t.status === "failed").length ? `, ${exec.templates.emails.filter((t) => t.status === "failed").length} failed` : ""}), ${exec.templates.sms.length} SMS.` : ""}${publishSummaryClause({ published: exec.published, publishFailed: exec.publishFailed, anyWorkflowCreated: exec.built.some((b) => b.type === "workflow" && b.status === "created") })}${exec.manual.some((m) => m.type === "calendar") || exec.calendarPending.length ? ` Calendars: ${exec.manual.filter((m) => m.type === "calendar").length} deferred to manual; ${new Set(exec.calendarPending.filter((c) => c.status === "pending").map((c) => c.workflowRef)).size} workflow(s) built with the calendar unpinned${exec.calendarPending.some((c) => c.status === "calendar_ready") ? `, ${new Set(exec.calendarPending.filter((c) => c.status === "calendar_ready").map((c) => c.workflowRef)).size} ready to pin` : ""}.` : ""}${exec.manual.some((m) => m.type === "calendar_team") ? ` ${exec.manual.filter((m) => m.type === "calendar_team").length} calendar(s) were built WITHOUT the people the plan named \u2014 see nextManualSteps; do not report those calendars as staffed.` : ""}${exec.missingSteps.length ? ` NOT COMPLETE: ${exec.missingSteps.reduce((n, w) => n + w.steps.length, 0)} step(s) the plan asks for are still missing from ${exec.missingSteps.length} workflow(s) that already existed \u2014 add them by hand (see missingSteps).` : ""}${exec.manual.length ? ` ${exec.manual.length} item(s) need a manual step (see nextManualSteps).` : ""}${exec.deferred.length ? " Deferred: " + exec.deferred.map((d) => `${d.count} ${d.section}`).join(", ") + " (manual)." : ""}` : `HALTED at ${exec.halted?.atRef} (${exec.halted?.reason}). ${exec.built.length} object(s) were created before the halt \u2014 see idMap to resume or clean up. NEVER-CLOBBER means a re-run will bind those, not duplicate them.`
25200
25238
  });
25201
25239
  }
25202
25240
  const collisions = result.items.filter((i) => i.status === "existing");
@@ -25616,6 +25654,7 @@ var init_intake_to_build = __esm({
25616
25654
  init_emails();
25617
25655
  init_user_provisioning();
25618
25656
  init_plan_form();
25657
+ init_publish_report();
25619
25658
  init_question_set();
25620
25659
  init_customization();
25621
25660
  init_intake_overlay();
@@ -26294,7 +26333,7 @@ var require_package = __commonJS({
26294
26333
  "package.json"(exports2, module2) {
26295
26334
  module2.exports = {
26296
26335
  name: "@elitedcs/ghl-mcp",
26297
- version: "3.72.2",
26336
+ version: "3.72.3",
26298
26337
  mcpName: "io.github.drjerryrelth/ghl-command",
26299
26338
  description: "GoHighLevel MCP Server for Claude. 247 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
26300
26339
  main: "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.72.2",
3
+ "version": "3.72.3",
4
4
  "mcpName": "io.github.drjerryrelth/ghl-command",
5
5
  "description": "GoHighLevel MCP Server for Claude. 247 tools — full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
6
6
  "main": "dist/index.js",
@@ -82,7 +82,7 @@
82
82
  "example_3days": {
83
83
  "type": "time",
84
84
  "startAfter": {
85
- "type": "day",
85
+ "type": "days",
86
86
  "value": 3,
87
87
  "when": "after"
88
88
  },
@@ -92,7 +92,7 @@
92
92
  "convertToMultipath": false,
93
93
  "transitions": []
94
94
  },
95
- "notes": "startAfter.type: 'minutes', 'hour', or 'day'. ALL 5 fields required (type, startAfter, isHybridAction, hybridActionType, transitions). Derived from working GHL UI-built workflows."
95
+ "notes": "startAfter.type: 'minutes', 'hour', or 'days' — NOT consistently singular, and 'day' is rejected with 400 INVALID_FIELD_VALUE on a workflow that has a real action before the wait (live-verified 2026-08-29). ALL 5 fields required (type, startAfter, isHybridAction, hybridActionType, transitions). Derived from working GHL UI-built workflows."
96
96
  },
97
97
  "internal_notification": {
98
98
  "example": {
@@ -243,7 +243,7 @@
243
243
  "actions": [
244
244
  { "name": "Add Reactivation Tag", "type": "add_contact_tag", "attributes": { "tags": ["reactivation-candidate"] } },
245
245
  { "name": "Send Reactivation SMS", "type": "sms", "attributes": { "body": "Hi {{contact.first_name}}, it's been a while since your last visit to {{business_name}}. We miss you! Book your next session and get 10% off.", "attachments": [] } },
246
- { "name": "Wait 3 Days", "type": "wait", "attributes": { "type": "time", "startAfter": { "type": "day", "value": 3, "when": "after" }, "name": "Wait", "isHybridAction": true, "hybridActionType": "wait", "convertToMultipath": false, "transitions": [] } },
246
+ { "name": "Wait 3 Days", "type": "wait", "attributes": { "type": "time", "startAfter": { "type": "days", "value": 3, "when": "after" }, "name": "Wait", "isHybridAction": true, "hybridActionType": "wait", "convertToMultipath": false, "transitions": [] } },
247
247
  { "name": "Send Reactivation Email", "type": "email", "attributes": { "subject": "We miss you, {{contact.first_name}}!", "html": "<p style=\"margin:0px;\">Hi {{contact.first_name}},</p><p style=\"margin:0px;\"><br></p><p style=\"margin:0px;\">It's been over 90 days since your last visit to {{business_name}}, and we wanted to check in.</p><p style=\"margin:0px;\"><br></p><p style=\"margin:0px;\">As a valued patient, we'd like to offer you 10% off your next treatment. Just mention this email when you book.</p><p style=\"margin:0px;\"><br></p><p style=\"margin:0px;\">See you soon!</p><p style=\"margin:0px;\">{{owner_name}}</p>", "trackingOptions": { "hasTrackingLinks": false, "hasUtmTracking": false, "hasTags": false } } }
248
248
  ]
249
249
  }