@elitedcs/ghl-mcp 3.78.0 → 3.78.1
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 +32 -0
- package/README.md +1 -1
- package/dist/index.js +50 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.78.1 — the template installer stops guessing which funnel it just made
|
|
4
|
+
|
|
5
|
+
Yesterday's release shipped `install_ghl_template` with the originals kept by default,
|
|
6
|
+
because the tool worked out which funnel it had created by comparing the account before
|
|
7
|
+
and after, and on a busy sub-account that comparison cannot be certain. Keeping the
|
|
8
|
+
originals was the safe answer to an open question.
|
|
9
|
+
|
|
10
|
+
The question now has an answer. GoHighLevel's install response names what it created: the
|
|
11
|
+
funnel id and its first page. The tool reads that id and waits for that funnel, so it
|
|
12
|
+
cannot land on one somebody else happened to be building at the same moment.
|
|
13
|
+
|
|
14
|
+
**It does not simply believe it.** The name only counts when your account agrees the
|
|
15
|
+
funnel is genuinely new, and the tool checks the response against a complete picture of
|
|
16
|
+
the account taken before the install. Two independent confirmations, because that one
|
|
17
|
+
field is what authorises removing anything. If the name and the account ever disagree, the
|
|
18
|
+
name is thrown away, nothing is removed, and the result says so.
|
|
19
|
+
|
|
20
|
+
With both confirmations in hand the originals go back to being removed once every branded
|
|
21
|
+
page has been read back and measured, which is how the tool was meant to work: one clean
|
|
22
|
+
funnel in your client's colours, not a doubled one you have to tidy. Pass
|
|
23
|
+
`keepOriginals: true` if you would rather see both.
|
|
24
|
+
|
|
25
|
+
Anything less and the originals stay, whatever you passed: no name in the response, a name
|
|
26
|
+
the account cannot corroborate, or an account with so many funnels that the before picture
|
|
27
|
+
was incomplete.
|
|
28
|
+
|
|
29
|
+
A split-test step, where one step holds more than one page, still stops any removal. Only
|
|
30
|
+
the first page of such a step is rebuilt, so removing the step would take the variants
|
|
31
|
+
with it.
|
|
32
|
+
|
|
33
|
+
Counts unchanged: 250 tools, 51 modules, free tier 112.
|
|
34
|
+
|
|
3
35
|
## 3.78.0 — GoHighLevel's own template library, repainted in your client's colours
|
|
4
36
|
|
|
5
37
|
GoHighLevel ships more than 900 designed funnel templates. You could always install
|
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -10713,6 +10713,15 @@ async function templateFacets(api, product = "funnels") {
|
|
|
10713
10713
|
const names = (list2) => (list2 ?? []).map((x) => typeof x === "string" ? x : x.name ?? "").filter(Boolean);
|
|
10714
10714
|
return { categories: names(out.categories), tags: names(out.tags), features: names(out.features) };
|
|
10715
10715
|
}
|
|
10716
|
+
function installedFunnelId(loaded) {
|
|
10717
|
+
const r = loaded;
|
|
10718
|
+
if (!r || typeof r !== "object") return void 0;
|
|
10719
|
+
if (r.status !== "ok") return void 0;
|
|
10720
|
+
if (r.data?.status !== "completed") return void 0;
|
|
10721
|
+
const t = r.data?.target;
|
|
10722
|
+
if (!t || t.asset !== "funnels") return void 0;
|
|
10723
|
+
return typeof t.assetId === "string" && GHL_ID.test(t.assetId) ? t.assetId : void 0;
|
|
10724
|
+
}
|
|
10716
10725
|
async function installTemplate(api, templateId) {
|
|
10717
10726
|
if (!/^[A-Za-z0-9]{12,40}$/.test(templateId)) throw new Error(`Not a template id: ${JSON.stringify(templateId)}`);
|
|
10718
10727
|
return call(api, "/templates/template/load", {
|
|
@@ -10737,12 +10746,13 @@ function rankForBrand(rows, brand) {
|
|
|
10737
10746
|
return { ...t, why: parts.join("; ") };
|
|
10738
10747
|
});
|
|
10739
10748
|
}
|
|
10740
|
-
var SVC;
|
|
10749
|
+
var SVC, GHL_ID;
|
|
10741
10750
|
var init_ghl_templates = __esm({
|
|
10742
10751
|
"src/command-os/design/ghl-templates.ts"() {
|
|
10743
10752
|
"use strict";
|
|
10744
10753
|
init_color();
|
|
10745
10754
|
SVC = "https://services.leadconnectorhq.com";
|
|
10755
|
+
GHL_ID = /^[A-Za-z0-9]{15,40}$/;
|
|
10746
10756
|
}
|
|
10747
10757
|
});
|
|
10748
10758
|
|
|
@@ -13550,7 +13560,7 @@ function installedFunnelTracker(before) {
|
|
|
13550
13560
|
};
|
|
13551
13561
|
}
|
|
13552
13562
|
function removalHoldBack(opts) {
|
|
13553
|
-
if (opts.truncated) return `this sub-account
|
|
13563
|
+
if (opts.truncated && !opts.identifiedFromResponse) return `this sub-account's funnels could not all be read back before the install, so "which funnel is new" could not be established with certainty`;
|
|
13554
13564
|
if (opts.multiPageStepNames.length > 0) return `${opts.multiPageStepNames.length} step(s) carry more than one page (${opts.multiPageStepNames.join(", ")}) and only the first of each was rebuilt`;
|
|
13555
13565
|
return null;
|
|
13556
13566
|
}
|
|
@@ -13563,12 +13573,23 @@ function registerPageStudioTools(server2, builderClient) {
|
|
|
13563
13573
|
locationId: () => client.locationId
|
|
13564
13574
|
});
|
|
13565
13575
|
const FUNNEL_PAGE = 100;
|
|
13576
|
+
const FUNNEL_PAGE_CAP = 20;
|
|
13566
13577
|
async function funnelSummary() {
|
|
13567
13578
|
const out = /* @__PURE__ */ new Map();
|
|
13568
|
-
|
|
13569
|
-
|
|
13570
|
-
|
|
13571
|
-
|
|
13579
|
+
let truncated = false;
|
|
13580
|
+
for (let page = 0; page < FUNNEL_PAGE_CAP; page++) {
|
|
13581
|
+
const r = await funnelRequest("GET", `/funnel/list?locationId=${client.locationId}&limit=${FUNNEL_PAGE}&offset=${page * FUNNEL_PAGE}`);
|
|
13582
|
+
const rows = r.funnels ?? [];
|
|
13583
|
+
const fresh = rows.filter((f) => !out.has(f._id));
|
|
13584
|
+
if (page > 0 && fresh.length === 0 && rows.length > 0) {
|
|
13585
|
+
truncated = true;
|
|
13586
|
+
break;
|
|
13587
|
+
}
|
|
13588
|
+
for (const f of rows) out.set(f._id, `${f.name} (${(f.steps ?? []).length} steps)`);
|
|
13589
|
+
if (rows.length < FUNNEL_PAGE) return { map: out, truncated: false };
|
|
13590
|
+
if (page === FUNNEL_PAGE_CAP - 1) truncated = true;
|
|
13591
|
+
}
|
|
13592
|
+
return { map: out, truncated };
|
|
13572
13593
|
}
|
|
13573
13594
|
async function funnelRequest(method, path36, body) {
|
|
13574
13595
|
const headers = await client.buildHeaders();
|
|
@@ -13723,23 +13744,34 @@ ${await response.text()}`);
|
|
|
13723
13744
|
templateId: import_zod42.z.string().describe("From find_ghl_template."),
|
|
13724
13745
|
brandSlug: import_zod42.z.string().optional().describe("A stored brand to repaint the template in. Omit to install it in the designer's own colours."),
|
|
13725
13746
|
confirm: import_zod42.z.literal("INSTALL").describe("Type INSTALL. This writes into the live sub-account you are switched to."),
|
|
13726
|
-
keepOriginals: import_zod42.z.boolean().optional().describe("With brandSlug: leave the template's original unbranded steps beside the branded ones.
|
|
13747
|
+
keepOriginals: import_zod42.z.boolean().optional().describe("With brandSlug: leave the template's original unbranded steps beside the branded ones. Normally they are removed once every branded page has been read back and measured. They are KEPT automatically, whatever you pass, if GoHighLevel did not name the funnel it created in its install response and the identification had to fall back to comparing the account before and after.")
|
|
13727
13748
|
},
|
|
13728
13749
|
async ({ templateId, brandSlug, keepOriginals }) => {
|
|
13729
13750
|
const brand = brandSlug ? houseLooks().find((x) => x.slug === brandSlug) ?? readBrand(slugify(brandSlug)) : null;
|
|
13730
13751
|
const inventory = await funnelSummary();
|
|
13731
13752
|
const before = inventory.map;
|
|
13732
|
-
await installTemplate(templateApi(), templateId);
|
|
13753
|
+
const named = installedFunnelId(await installTemplate(templateApi(), templateId));
|
|
13754
|
+
const namedFunnelId = named && !before.has(named) ? named : void 0;
|
|
13755
|
+
const namedButNotNew = Boolean(named) && named !== namedFunnelId;
|
|
13733
13756
|
let funnel;
|
|
13734
13757
|
let ambiguous = [];
|
|
13735
13758
|
const tracker = installedFunnelTracker(before);
|
|
13736
13759
|
for (let tries = 0; tries < 8; tries++) {
|
|
13737
13760
|
const list2 = await funnelRequest("GET", `/funnel/list?locationId=${client.locationId}&limit=${FUNNEL_PAGE}`);
|
|
13738
|
-
const
|
|
13739
|
-
if (
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13761
|
+
const rows = list2.funnels ?? [];
|
|
13762
|
+
if (namedFunnelId) {
|
|
13763
|
+
const mine = rows.find((f) => f._id === namedFunnelId);
|
|
13764
|
+
if (mine && (mine.steps ?? []).length > 0) {
|
|
13765
|
+
funnel = mine;
|
|
13766
|
+
break;
|
|
13767
|
+
}
|
|
13768
|
+
} else {
|
|
13769
|
+
const seen = tracker.observe(rows);
|
|
13770
|
+
if (seen.settled) {
|
|
13771
|
+
if ("funnel" in seen) funnel = seen.funnel;
|
|
13772
|
+
else ambiguous = seen.ambiguous;
|
|
13773
|
+
break;
|
|
13774
|
+
}
|
|
13743
13775
|
}
|
|
13744
13776
|
await new Promise((r) => setTimeout(r, 1500));
|
|
13745
13777
|
}
|
|
@@ -13839,9 +13871,10 @@ ${await response.text()}`);
|
|
|
13839
13871
|
}
|
|
13840
13872
|
}
|
|
13841
13873
|
let removed = 0;
|
|
13842
|
-
const
|
|
13874
|
+
const certain = Boolean(namedFunnelId) && !inventory.truncated;
|
|
13875
|
+
const removeOriginals = certain ? keepOriginals !== true : keepOriginals === false;
|
|
13843
13876
|
const complete = problems.length === 0 && done.length === originals.length && done.length > 0;
|
|
13844
|
-
const holdBack = removalHoldBack({ truncated: inventory.truncated, funnelPageSize: FUNNEL_PAGE, multiPageStepNames: multiPage.map((m) => m.name) });
|
|
13877
|
+
const holdBack = removalHoldBack({ truncated: inventory.truncated, funnelPageSize: FUNNEL_PAGE, multiPageStepNames: multiPage.map((m) => m.name), identifiedFromResponse: certain });
|
|
13845
13878
|
if (complete && removeOriginals && holdBack === null) {
|
|
13846
13879
|
for (const d of done) {
|
|
13847
13880
|
try {
|
|
@@ -13860,6 +13893,7 @@ ${await response.text()}`);
|
|
|
13860
13893
|
pages: done,
|
|
13861
13894
|
originalsRemoved: complete && removeOriginals ? removed : 0,
|
|
13862
13895
|
...problems.length > 0 && { problems, kept: "Every original step was LEFT IN PLACE because something failed. Nothing was deleted." },
|
|
13896
|
+
...namedButNotNew && { identification: "GoHighLevel named a funnel that this account ALREADY had before the install. That name was discarded and the account was compared instead, so the funnel above was identified the cautious way and originals are only removed if you asked for it explicitly. Worth reporting: it means the install response no longer means what it did." },
|
|
13863
13897
|
...holdBack !== null && { kept: `Every original step was LEFT IN PLACE on purpose: ${holdBack}. The branded pages are there beside them \u2014 remove the originals by hand once you have looked.` },
|
|
13864
13898
|
warnings: rethemeEnvelope({}, brand, palette).warnings,
|
|
13865
13899
|
next: done.length ? `Grade one before the client sees it: verify_design on ${String(done[0].preview)} with medium "site" and placement "ghl". GoHighLevel's own templates are designed but frequently fail contrast and tap-target rules.` : "Nothing was branded.",
|
|
@@ -38775,7 +38809,7 @@ var require_package = __commonJS({
|
|
|
38775
38809
|
"package.json"(exports2, module2) {
|
|
38776
38810
|
module2.exports = {
|
|
38777
38811
|
name: "@elitedcs/ghl-mcp",
|
|
38778
|
-
version: "3.78.
|
|
38812
|
+
version: "3.78.1",
|
|
38779
38813
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
38780
38814
|
description: "GoHighLevel MCP Server for Claude. 250 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.",
|
|
38781
38815
|
main: "dist/index.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.78.
|
|
3
|
+
"version": "3.78.1",
|
|
4
4
|
"mcpName": "io.github.drjerryrelth/ghl-command",
|
|
5
5
|
"description": "GoHighLevel MCP Server for Claude. 250 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",
|