@elitedcs/ghl-mcp 3.83.2 → 3.83.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 +28 -0
- package/README.md +1 -1
- package/dist/index.js +65 -9
- package/guide/guide.html +4 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 3.83.3 — an account you marked protected is now actually protected
|
|
6
|
+
|
|
7
|
+
**Marking a client account protected did not stop fourteen write paths from writing to it.**
|
|
8
|
+
The guard was real and it was in the right place for the public API, but this product also
|
|
9
|
+
writes to GoHighLevel through its own builder connection, and those calls never passed it.
|
|
10
|
+
So a build could create a pipeline and a form on an account you had explicitly told it never
|
|
11
|
+
to touch, and nothing would say so. Pipelines and forms are the first two things a build
|
|
12
|
+
creates. Every one of those paths now goes through the same check, including the template
|
|
13
|
+
installer, which writes a funnel and its pages — the case that matters most, because
|
|
14
|
+
GoHighLevel keeps only the first content save on a page.
|
|
15
|
+
|
|
16
|
+
**Verify no longer needs your own Anthropic key before it will let you start.** A new client
|
|
17
|
+
with no key met a disabled Run button, while the same screen said most people do not need to
|
|
18
|
+
paste anything there. Both were true at once: runs use whatever you are signed in to Claude
|
|
19
|
+
with and cost nothing extra, and the readiness check demanded a key anyway. The key is
|
|
20
|
+
reported now, not required. If you have one saved, runs bill it; if you do not, they use your
|
|
21
|
+
Claude sign-in.
|
|
22
|
+
|
|
23
|
+
**The Verify tile stopped promising something it could not do.** It said it submits a test
|
|
24
|
+
lead and deletes it. Since funnels left the build, no plan it creates has anywhere to send
|
|
25
|
+
one, and the report a screen later said so. It now says what it actually does for every plan,
|
|
26
|
+
and still discloses the test lead for the accounts that can take one.
|
|
27
|
+
|
|
28
|
+
**An older install can no longer roll your skills backwards.** The installer refreshed any
|
|
29
|
+
skill file it had written before, without checking whether the version doing the writing was
|
|
30
|
+
older than the one that wrote it. Running an older copy alongside a newer one quietly reverted
|
|
31
|
+
your skills. It refuses now and tells you it kept the newer ones.
|
|
32
|
+
|
|
5
33
|
## 3.83.2 — the static design-rule harness and its fixtures leave the package
|
|
6
34
|
|
|
7
35
|
A development harness and its four HTML fixtures were in the published package in
|
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -7437,6 +7437,7 @@ function registerEmailBuilderInternalTools(server2, builderClient, publicClient)
|
|
|
7437
7437
|
const client = builderClient;
|
|
7438
7438
|
async function builderRequest(method, path39, body) {
|
|
7439
7439
|
const headers = await client.buildHeaders();
|
|
7440
|
+
assertWriteAllowed(method, `${EMAIL_BUILDER_BASE}${path39}`, { defaultLocationId: client.locationId });
|
|
7440
7441
|
const response = await fetch(`${EMAIL_BUILDER_BASE}${path39}`, {
|
|
7441
7442
|
method,
|
|
7442
7443
|
headers,
|
|
@@ -7542,6 +7543,7 @@ var init_emails = __esm({
|
|
|
7542
7543
|
init_account_reads();
|
|
7543
7544
|
import_zod28 = require("zod");
|
|
7544
7545
|
init_tool_helpers();
|
|
7546
|
+
init_account_rules();
|
|
7545
7547
|
EMAIL_BUILDER_BASE = "https://backend.leadconnectorhq.com/emails/builder";
|
|
7546
7548
|
SNIPPETS_BASE = "https://services.leadconnectorhq.com/snippets";
|
|
7547
7549
|
TEMPLATE_TYPES = ["html", "folder", "import", "builder", "blank", "ai_template", "vibe-editor"];
|
|
@@ -10289,12 +10291,15 @@ function readId(workflow) {
|
|
|
10289
10291
|
const id = workflow._id ?? record.id;
|
|
10290
10292
|
return typeof id === "string" ? id : "";
|
|
10291
10293
|
}
|
|
10294
|
+
function attributeBag(attributes) {
|
|
10295
|
+
return attributes ? Object.fromEntries(Object.entries(attributes)) : {};
|
|
10296
|
+
}
|
|
10292
10297
|
function summarizeWebhookWorkflow(workflow) {
|
|
10293
10298
|
const templates = workflow.workflowData?.templates ?? [];
|
|
10294
10299
|
const webhookActions = templates.filter((action) => action.type === "webhook");
|
|
10295
10300
|
if (!webhookActions.length) return null;
|
|
10296
10301
|
const actions = webhookActions.map((action) => {
|
|
10297
|
-
const attributes = action.attributes
|
|
10302
|
+
const attributes = attributeBag(action.attributes);
|
|
10298
10303
|
return {
|
|
10299
10304
|
actionId: action.id,
|
|
10300
10305
|
name: action.name,
|
|
@@ -10451,7 +10456,7 @@ function registerWebhookTools(server2, builderClient) {
|
|
|
10451
10456
|
}
|
|
10452
10457
|
const target = actionId ? webhookActions.find((a) => a.id === actionId) : webhookActions[0];
|
|
10453
10458
|
if (!target) throw new Error(`No webhook action with id ${actionId} on workflow ${webhookId}.`);
|
|
10454
|
-
const attributes = target.attributes
|
|
10459
|
+
const attributes = attributeBag(target.attributes);
|
|
10455
10460
|
const validated = buildWebhookAction({
|
|
10456
10461
|
id: target.id,
|
|
10457
10462
|
name: target.name,
|
|
@@ -11507,6 +11512,7 @@ ${text2}`);
|
|
|
11507
11512
|
headers.Origin = "https://page-builder.leadconnectorhq.com";
|
|
11508
11513
|
headers.Referer = "https://page-builder.leadconnectorhq.com/";
|
|
11509
11514
|
const url = `https://backend.leadconnectorhq.com/funnels/builder/autosave/${pageId}`;
|
|
11515
|
+
assertWriteAllowed("POST", url, { defaultLocationId: client.locationId });
|
|
11510
11516
|
const response = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) });
|
|
11511
11517
|
if (!response.ok) {
|
|
11512
11518
|
throw new Error(`Funnel API Error ${response.status}: POST /builder/autosave/${pageId}
|
|
@@ -11615,6 +11621,7 @@ var init_funnel_builder = __esm({
|
|
|
11615
11621
|
import_zod41 = require("zod");
|
|
11616
11622
|
init_tool_helpers();
|
|
11617
11623
|
init_page_reads();
|
|
11624
|
+
init_account_rules();
|
|
11618
11625
|
}
|
|
11619
11626
|
});
|
|
11620
11627
|
|
|
@@ -11815,6 +11822,9 @@ function requireCompany(api) {
|
|
|
11815
11822
|
}
|
|
11816
11823
|
async function call(api, path39, init) {
|
|
11817
11824
|
const headers = { ...await api.headers(), Origin: "https://app.gohighlevel.com", Referer: "https://app.gohighlevel.com/" };
|
|
11825
|
+
if (MUTATING_PATHS.some((mutating) => path39.startsWith(mutating))) {
|
|
11826
|
+
assertWriteAllowed("POST", SVC + path39, { defaultLocationId: api.locationId() });
|
|
11827
|
+
}
|
|
11818
11828
|
const doFetch = api.fetch ?? fetch;
|
|
11819
11829
|
const response = await doFetch(SVC + path39, { ...init, headers });
|
|
11820
11830
|
const text = await response.text();
|
|
@@ -11908,12 +11918,14 @@ function rankForBrand(rows, brand) {
|
|
|
11908
11918
|
return { ...t, why: parts.join("; ") };
|
|
11909
11919
|
});
|
|
11910
11920
|
}
|
|
11911
|
-
var SVC, GHL_ID;
|
|
11921
|
+
var SVC, MUTATING_PATHS, GHL_ID;
|
|
11912
11922
|
var init_ghl_templates = __esm({
|
|
11913
11923
|
"src/command-os/design/ghl-templates.ts"() {
|
|
11914
11924
|
"use strict";
|
|
11915
11925
|
init_color();
|
|
11926
|
+
init_account_rules();
|
|
11916
11927
|
SVC = "https://services.leadconnectorhq.com";
|
|
11928
|
+
MUTATING_PATHS = ["/templates/template/load"];
|
|
11917
11929
|
GHL_ID = /^[A-Za-z0-9]{15,40}$/;
|
|
11918
11930
|
}
|
|
11919
11931
|
});
|
|
@@ -16488,7 +16500,9 @@ ${await response.text()}`);
|
|
|
16488
16500
|
const headers = await client.buildHeaders();
|
|
16489
16501
|
headers.Origin = "https://page-builder.leadconnectorhq.com";
|
|
16490
16502
|
headers.Referer = "https://page-builder.leadconnectorhq.com/";
|
|
16491
|
-
const
|
|
16503
|
+
const autosaveUrl = `https://backend.leadconnectorhq.com/funnels/builder/autosave/${pageId}`;
|
|
16504
|
+
assertWriteAllowed("POST", autosaveUrl, { defaultLocationId: client.locationId });
|
|
16505
|
+
const response = await fetch(autosaveUrl, {
|
|
16492
16506
|
method: "POST",
|
|
16493
16507
|
headers,
|
|
16494
16508
|
body: JSON.stringify(body)
|
|
@@ -16985,6 +16999,7 @@ var init_page_studio = __esm({
|
|
|
16985
16999
|
init_tool_helpers();
|
|
16986
17000
|
init_compose();
|
|
16987
17001
|
init_website();
|
|
17002
|
+
init_account_rules();
|
|
16988
17003
|
valueCardSchema = import_zod43.z.object({
|
|
16989
17004
|
title: import_zod43.z.string().describe("Card title (short, uppercase reads best)."),
|
|
16990
17005
|
bodyHtml: import_zod43.z.string().describe("Card body as HTML (<p>\u2026</p>). May use {{location.*}} merge fields."),
|
|
@@ -17075,6 +17090,7 @@ function cleanFormDataForSave(formData) {
|
|
|
17075
17090
|
async function formApiRequest(client, method, path39, body) {
|
|
17076
17091
|
const headers = await client.buildHeaders();
|
|
17077
17092
|
const url = `https://backend.leadconnectorhq.com/forms${path39}`;
|
|
17093
|
+
assertWriteAllowed(method, url, { defaultLocationId: client.locationId });
|
|
17078
17094
|
const options = { method, headers };
|
|
17079
17095
|
if (body && (method === "POST" || method === "PUT")) {
|
|
17080
17096
|
options.body = JSON.stringify(body);
|
|
@@ -17277,6 +17293,7 @@ var init_form_builder = __esm({
|
|
|
17277
17293
|
"use strict";
|
|
17278
17294
|
import_zod44 = require("zod");
|
|
17279
17295
|
init_tool_helpers();
|
|
17296
|
+
init_account_rules();
|
|
17280
17297
|
}
|
|
17281
17298
|
});
|
|
17282
17299
|
|
|
@@ -17671,6 +17688,7 @@ function registerPipelineBuilderTools(server2, builderClient) {
|
|
|
17671
17688
|
async function pipelineRequest(method, path39, body) {
|
|
17672
17689
|
const headers = await client.buildHeaders();
|
|
17673
17690
|
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path39}`;
|
|
17691
|
+
assertWriteAllowed(method, url, { defaultLocationId: client.locationId });
|
|
17674
17692
|
const options = { method, headers };
|
|
17675
17693
|
if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
|
17676
17694
|
options.body = JSON.stringify(body);
|
|
@@ -17853,6 +17871,7 @@ var init_pipeline_builder = __esm({
|
|
|
17853
17871
|
"src/tools/pipeline-builder.ts"() {
|
|
17854
17872
|
"use strict";
|
|
17855
17873
|
import_zod46 = require("zod");
|
|
17874
|
+
init_account_rules();
|
|
17856
17875
|
}
|
|
17857
17876
|
});
|
|
17858
17877
|
|
|
@@ -26972,8 +26991,13 @@ async function clientPreflight(registry2, locationId2) {
|
|
|
26972
26991
|
}
|
|
26973
26992
|
} catch {
|
|
26974
26993
|
}
|
|
26975
|
-
lines.push({
|
|
26976
|
-
|
|
26994
|
+
lines.push({
|
|
26995
|
+
key: "authentication",
|
|
26996
|
+
ok: authenticated,
|
|
26997
|
+
text: authenticated ? "Your Anthropic key authenticates, so powered runs will bill it." : key ? "The saved Anthropic key did not authenticate, so powered runs will fall back to your Claude sign-in on this computer. Replace it in Your agency, or remove it." : "No Anthropic key saved. Powered runs use your Claude sign-in on this computer and cost you nothing extra."
|
|
26998
|
+
});
|
|
26999
|
+
const blocking = lines.filter((l) => l.key !== "authentication");
|
|
27000
|
+
return { ok: blocking.every((l) => l.ok), lines };
|
|
26977
27001
|
}
|
|
26978
27002
|
async function agencyConnectionReady(registry2, companyId) {
|
|
26979
27003
|
const key = registry2.getAgencyKey();
|
|
@@ -28557,7 +28581,7 @@ var require_package = __commonJS({
|
|
|
28557
28581
|
"package.json"(exports2, module2) {
|
|
28558
28582
|
module2.exports = {
|
|
28559
28583
|
name: "@elitedcs/ghl-mcp",
|
|
28560
|
-
version: "3.83.
|
|
28584
|
+
version: "3.83.3",
|
|
28561
28585
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
28562
28586
|
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.",
|
|
28563
28587
|
main: "dist/index.js",
|
|
@@ -35206,7 +35230,7 @@ function card(c,state,run){const st=(state.clients[c.locationId]||{stages:STAGES
|
|
|
35206
35230
|
el.appendChild(box)}
|
|
35207
35231
|
// One short line here; the button's confirmation carries the full wording. At 135
|
|
35208
35232
|
// characters this sentence made every stage box 677 px tall (auditor, 2026-09-07).
|
|
35209
|
-
if(i===4){const disclosure=document.createElement("small");disclosure.textContent="
|
|
35233
|
+
if(i===4){const disclosure=document.createElement("small");disclosure.textContent="Reads every planned item back. May submit and delete one test lead.";el.appendChild(disclosure)}
|
|
35210
35234
|
if(gate)el.title=gate;
|
|
35211
35235
|
if(stale&&!live){const reason=document.createElement("small");reason.textContent=stale.reason;el.appendChild(reason)}
|
|
35212
35236
|
const oc=(state.outcomes||{})[c.locationId+":"+i];
|
|
@@ -39194,6 +39218,7 @@ function registerSmartListTools(server2, builderClient) {
|
|
|
39194
39218
|
async function smartListRequest(method, path39, body) {
|
|
39195
39219
|
const headers = await client.buildHeaders();
|
|
39196
39220
|
const url = `${SMARTLIST_BASE}${path39}`;
|
|
39221
|
+
assertWriteAllowed(method, url, { defaultLocationId: client.locationId });
|
|
39197
39222
|
const options = { method, headers };
|
|
39198
39223
|
if (body && (method === "POST" || method === "PUT")) {
|
|
39199
39224
|
options.body = JSON.stringify(body);
|
|
@@ -39263,6 +39288,7 @@ ${await res.text()}`);
|
|
|
39263
39288
|
];
|
|
39264
39289
|
const body = buildContactSmartListBody({ listName: name, locationId: loc, filters });
|
|
39265
39290
|
const headers = await client.buildHeaders();
|
|
39291
|
+
assertWriteAllowed("POST", CONTACT_SMARTLIST_BASE, { defaultLocationId: client.locationId });
|
|
39266
39292
|
const res = await fetch(CONTACT_SMARTLIST_BASE, { method: "POST", headers, body: JSON.stringify(body) });
|
|
39267
39293
|
if (!res.ok) throw new Error(`Smart Lists API Error ${res.status}: POST /contacts/smartlist
|
|
39268
39294
|
${await res.text()}`);
|
|
@@ -39300,6 +39326,7 @@ ${await res.text()}`);
|
|
|
39300
39326
|
async ({ listId }) => {
|
|
39301
39327
|
try {
|
|
39302
39328
|
const headers = await client.buildHeaders();
|
|
39329
|
+
assertWriteAllowed("POST", CONTACT_SMARTLIST_DELETE, { defaultLocationId: client.locationId });
|
|
39303
39330
|
const res = await fetch(CONTACT_SMARTLIST_DELETE, {
|
|
39304
39331
|
method: "POST",
|
|
39305
39332
|
headers,
|
|
@@ -39413,6 +39440,7 @@ var init_smart_lists = __esm({
|
|
|
39413
39440
|
"use strict";
|
|
39414
39441
|
import_zod57 = require("zod");
|
|
39415
39442
|
init_tool_helpers();
|
|
39443
|
+
init_account_rules();
|
|
39416
39444
|
SMARTLIST_BASE = "https://backend.leadconnectorhq.com/lists/dynamic";
|
|
39417
39445
|
OBJECT_KEYS = ["contacts", "opportunity"];
|
|
39418
39446
|
CONTACT_SMARTLIST_BASE = "https://services.leadconnectorhq.com/contacts/smartlist";
|
|
@@ -39434,6 +39462,7 @@ function registerReputationTools(server2, builderClient) {
|
|
|
39434
39462
|
if (!builderClient) return;
|
|
39435
39463
|
const client = builderClient;
|
|
39436
39464
|
async function reputationRequest(method, path39) {
|
|
39465
|
+
assertWriteAllowed(method, `${REPUTATION_BASE}${path39}`, { defaultLocationId: client.locationId });
|
|
39437
39466
|
const headers = await client.buildHeaders();
|
|
39438
39467
|
const response = await fetch(`${REPUTATION_BASE}${path39}`, { method, headers });
|
|
39439
39468
|
if (!response.ok) {
|
|
@@ -39512,6 +39541,7 @@ var init_reputation = __esm({
|
|
|
39512
39541
|
"use strict";
|
|
39513
39542
|
import_zod58 = require("zod");
|
|
39514
39543
|
init_tool_helpers();
|
|
39544
|
+
init_account_rules();
|
|
39515
39545
|
REPUTATION_BASE = "https://backend.leadconnectorhq.com/reputation";
|
|
39516
39546
|
}
|
|
39517
39547
|
});
|
|
@@ -39546,6 +39576,7 @@ function registerEmailCampaignTools(server2, builderClient) {
|
|
|
39546
39576
|
if (enableResendToUnopened !== void 0) body.enableResendToUnopened = enableResendToUnopened;
|
|
39547
39577
|
if (hasUtmTracking !== void 0) body.hasUtmTracking = hasUtmTracking;
|
|
39548
39578
|
const headers = await client.buildHeaders();
|
|
39579
|
+
assertWriteAllowed("POST", `${SVC_BASE}/emails/schedule`, { defaultLocationId: client.locationId });
|
|
39549
39580
|
const response = await fetch(`${SVC_BASE}/emails/schedule`, {
|
|
39550
39581
|
method: "POST",
|
|
39551
39582
|
headers,
|
|
@@ -39571,6 +39602,7 @@ var init_email_campaigns = __esm({
|
|
|
39571
39602
|
"use strict";
|
|
39572
39603
|
import_zod59 = require("zod");
|
|
39573
39604
|
init_tool_helpers();
|
|
39605
|
+
init_account_rules();
|
|
39574
39606
|
SVC_BASE = "https://services.leadconnectorhq.com";
|
|
39575
39607
|
}
|
|
39576
39608
|
});
|
|
@@ -39580,6 +39612,7 @@ function registerMembershipTools(server2, builderClient) {
|
|
|
39580
39612
|
if (!builderClient) return;
|
|
39581
39613
|
const client = builderClient;
|
|
39582
39614
|
async function membershipRequest(path39, method = "GET", body) {
|
|
39615
|
+
assertWriteAllowed(method, `${MEMBERSHIP_BASE2}${path39}`, { defaultLocationId: client.locationId });
|
|
39583
39616
|
const headers = await client.buildHeaders();
|
|
39584
39617
|
const response = await fetch(`${MEMBERSHIP_BASE2}${path39}`, {
|
|
39585
39618
|
method,
|
|
@@ -39767,6 +39800,7 @@ var init_memberships = __esm({
|
|
|
39767
39800
|
"use strict";
|
|
39768
39801
|
import_zod60 = require("zod");
|
|
39769
39802
|
init_tool_helpers();
|
|
39803
|
+
init_account_rules();
|
|
39770
39804
|
MEMBERSHIP_BASE2 = "https://backend.leadconnectorhq.com/membership";
|
|
39771
39805
|
}
|
|
39772
39806
|
});
|
|
@@ -40115,6 +40149,7 @@ Pipelines: ${template.pipelines.length} to create`);
|
|
|
40115
40149
|
} else {
|
|
40116
40150
|
try {
|
|
40117
40151
|
const headers = await builderClient.buildHeaders();
|
|
40152
|
+
assertWriteAllowed("POST", "https://backend.leadconnectorhq.com/opportunities/pipelines", { defaultLocationId: locId });
|
|
40118
40153
|
const res = await fetch("https://backend.leadconnectorhq.com/opportunities/pipelines", {
|
|
40119
40154
|
method: "POST",
|
|
40120
40155
|
headers,
|
|
@@ -40289,6 +40324,7 @@ var init_template_deployer = __esm({
|
|
|
40289
40324
|
init_tool_helpers();
|
|
40290
40325
|
fs28 = __toESM(require("fs"));
|
|
40291
40326
|
path27 = __toESM(require("path"));
|
|
40327
|
+
init_account_rules();
|
|
40292
40328
|
TemplateSchema = import_zod61.z.object({
|
|
40293
40329
|
templateName: import_zod61.z.string(),
|
|
40294
40330
|
templateVersion: import_zod61.z.string().optional(),
|
|
@@ -50950,6 +50986,7 @@ function pickCalendarPins(workflow) {
|
|
|
50950
50986
|
}
|
|
50951
50987
|
function makeFirebaseRequest(builderClient) {
|
|
50952
50988
|
return async (url, method, body) => {
|
|
50989
|
+
assertWriteAllowed(method, url, { defaultLocationId: builderClient.locationId });
|
|
50953
50990
|
const headers = await builderClient.buildHeaders();
|
|
50954
50991
|
const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : void 0 });
|
|
50955
50992
|
if (!response.ok) {
|
|
@@ -52123,6 +52160,7 @@ var init_intake_to_build = __esm({
|
|
|
52123
52160
|
init_plan_from_preset();
|
|
52124
52161
|
init_executor();
|
|
52125
52162
|
init_execute();
|
|
52163
|
+
init_account_rules();
|
|
52126
52164
|
customFieldItemSchema = import_zod80.z.object({
|
|
52127
52165
|
id: import_zod80.z.string(),
|
|
52128
52166
|
name: import_zod80.z.string(),
|
|
@@ -52658,6 +52696,15 @@ var os6 = __toESM(require("node:os"));
|
|
|
52658
52696
|
function sha2562(buf) {
|
|
52659
52697
|
return (0, import_node_crypto12.createHash)("sha256").update(buf).digest("hex");
|
|
52660
52698
|
}
|
|
52699
|
+
function isOlderRelease(candidate, installed) {
|
|
52700
|
+
const parts = (v) => v.trim().split(".").map((n) => Number.parseInt(n, 10));
|
|
52701
|
+
const a = parts(candidate), b = parts(installed);
|
|
52702
|
+
if (a.length !== 3 || b.length !== 3 || [...a, ...b].some(Number.isNaN)) return false;
|
|
52703
|
+
for (let i = 0; i < 3; i++) {
|
|
52704
|
+
if (a[i] !== b[i]) return a[i] < b[i];
|
|
52705
|
+
}
|
|
52706
|
+
return false;
|
|
52707
|
+
}
|
|
52661
52708
|
function bundledSkillsDir(baseDir) {
|
|
52662
52709
|
const candidate = path37.resolve(baseDir, "..", "skills");
|
|
52663
52710
|
try {
|
|
@@ -52691,6 +52738,7 @@ function installBundledSkills(opts) {
|
|
|
52691
52738
|
installed: [],
|
|
52692
52739
|
updated: [],
|
|
52693
52740
|
skippedUserModified: [],
|
|
52741
|
+
skippedOlderPackage: [],
|
|
52694
52742
|
unchanged: [],
|
|
52695
52743
|
errors: [],
|
|
52696
52744
|
targetDir: opts.targetDir ?? path37.join(os6.homedir(), ".claude", "skills"),
|
|
@@ -52733,7 +52781,11 @@ function installBundledSkills(opts) {
|
|
|
52733
52781
|
continue;
|
|
52734
52782
|
}
|
|
52735
52783
|
const lastWritten = marker.files[rel];
|
|
52736
|
-
if (lastWritten && destHash === lastWritten) {
|
|
52784
|
+
if (lastWritten && destHash === lastWritten && isOlderRelease(opts.packageVersion, marker.packageVersion)) {
|
|
52785
|
+
newMarker.files[rel] = destHash;
|
|
52786
|
+
newMarker.packageVersion = marker.packageVersion;
|
|
52787
|
+
result.skippedOlderPackage.push(rel);
|
|
52788
|
+
} else if (lastWritten && destHash === lastWritten) {
|
|
52737
52789
|
fs38.writeFileSync(dest, src);
|
|
52738
52790
|
newMarker.files[rel] = srcHash;
|
|
52739
52791
|
result.updated.push(rel);
|
|
@@ -52757,6 +52809,7 @@ function summarizeInstall(r) {
|
|
|
52757
52809
|
if (r.installed.length) bits.push(`${r.installed.length} installed`);
|
|
52758
52810
|
if (r.updated.length) bits.push(`${r.updated.length} updated`);
|
|
52759
52811
|
if (r.skippedUserModified.length) bits.push(`${r.skippedUserModified.length} kept (your edits)`);
|
|
52812
|
+
if (r.skippedOlderPackage.length) bits.push(`${r.skippedOlderPackage.length} kept (newer than this version)`);
|
|
52760
52813
|
if (r.errors.length) bits.push(`${r.errors.length} errors`);
|
|
52761
52814
|
if (bits.length === 0) return "";
|
|
52762
52815
|
return `Skills: ${bits.join(", ")} \u2192 ${r.targetDir}`;
|
|
@@ -52786,6 +52839,9 @@ function registerSkillsTool(server2, packageVersion, baseDir) {
|
|
|
52786
52839
|
installed: result.installed,
|
|
52787
52840
|
updated: result.updated,
|
|
52788
52841
|
keptYourEdits: result.skippedUserModified,
|
|
52842
|
+
// A member running an older install alongside a newer one would otherwise
|
|
52843
|
+
// have their skills rolled back without a word. BUG-2026-09-10-01.
|
|
52844
|
+
keptNewerThanThisVersion: result.skippedOlderPackage,
|
|
52789
52845
|
unchanged: result.unchanged.length,
|
|
52790
52846
|
errors: result.errors,
|
|
52791
52847
|
next: result.installed.length > 0 || result.updated.length > 0 ? "Fully restart Claude (quit and reopen) so the new/updated skills load." : "Everything already current."
|
package/guide/guide.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>GHL Command — User Guide</title>
|
|
7
|
-
<!-- guides-hash:
|
|
7
|
+
<!-- guides-hash: 85b384b5142e9278 -->
|
|
8
8
|
<style>
|
|
9
9
|
/* Deliberately light in every environment, including a dark-mode OS. This is a
|
|
10
10
|
reference document people read at length, print, and save to PDF, and a page
|
|
@@ -677,20 +677,21 @@ Then tell me which ones you can fix safely and which ones need a decision from m
|
|
|
677
677
|
<div class="pro"><div class="pro-head"><b>The exact ask we run on real client accounts</b><span class="pill full">Proven live</span><button class="copy" type="button">Copy</button></div><p class="pro-body">Run the GHL Command Blueprint for a new client.
|
|
678
678
|
The business: [Radiance Med Spa], a [med spa] in [Austin, Texas]. What they sell: [body contouring and injectables]. Their main goal for the next 90 days: [fill the consult calendar].
|
|
679
679
|
Install the intake form in my [MCP Testing] account, give me the link to send them, and once their answers are in, build the plan from those answers.
|
|
680
|
-
Show me the full plan before anything is created: pipeline and stages, custom fields, tags, calendar, form,
|
|
680
|
+
Show me the full plan before anything is created: pipeline and stages, custom fields, tags, calendar, form, the emails and texts written out, and every workflow. Mark clearly which items you will build and which ones I have to do myself in GHL.
|
|
681
681
|
Build nothing until I say approved.</p></div>
|
|
682
682
|
<p class="gpara">Everything in [brackets] is yours to change. The last line is the gate. The Blueprint is designed to stop there and wait for a human.</p>
|
|
683
683
|
</div>
|
|
684
684
|
<div class="gsec"><h2>What happens when you ask</h2>
|
|
685
685
|
<ol class="steps">
|
|
686
686
|
<li>Claude installs the intake form in the account and gives you the link to send the client.</li>
|
|
687
|
-
<li>When their answers land, it turns them into a build plan: pipeline, fields, tags, calendar, form,
|
|
687
|
+
<li>When their answers land, it turns them into a build plan: pipeline, fields, tags, calendar, form, messages, workflows. Every message written out in full. The answers arrive as a <b>contact</b> in the account, tagged <code>blueprint-intake</code>, carrying every question as a field on that contact. That is where Claude reads them from.</li>
|
|
688
688
|
<li>You read it and change anything in plain English. Then you approve, and only then does it start building. Before your approval the plan is a dry run: it resolves every reference and reports what it would do, and writes none of it.</li>
|
|
689
689
|
<li>It builds in order, checks each piece exists after it creates it, and hands you the manual checklist for the parts that need a human in GHL.</li>
|
|
690
690
|
</ol>
|
|
691
691
|
</div>
|
|
692
692
|
<div class="gsec"><h2>Good to know</h2>
|
|
693
693
|
<ul>
|
|
694
|
+
<li><b>The Blueprint does not build a funnel, a page, or a website, and that is deliberate.</b> GoHighLevel's own page builder was never good enough to hand a client, so the build stops at the account: pipeline, fields, tags, calendar, form, messages and workflows. A plan that asks for a funnel is refused rather than half-built. <b>You have not lost funnels.</b> GHL Command still installs one of GoHighLevel's own designed funnels from its library (see *Build a funnel from a template*), still creates funnels and pages directly, and Page Studio still composes pages and whole sites. Pages are simply their own job, after the account exists, on hosting you choose.</li>
|
|
694
695
|
<li>Triggers get scoped to the thing you actually meant. An appointment trigger can be tied to one calendar, so a caregiver interview does not start the family consultation sequence. A reply trigger can be narrowed to a tag or to whether the reply was a yes or a no, instead of firing on every inbound message. Missed calls can be caught by call outcome, and failed phone-number checks by validation state. Say which one you mean in plain English and the plan carries it through.</li>
|
|
695
696
|
<li>If a workflow step moves a deal to a new stage, the plan makes it find the deal first. That pairing is checked before the build runs, because a stage move on its own saves cleanly and then does nothing at all.</li>
|
|
696
697
|
<li>The manual list is short but real: connecting a payment provider, phone and A2P registration, connecting social accounts, and domain settings. Those need a human with account access, and no tool can honestly do them for you.</li>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.83.
|
|
3
|
+
"version": "3.83.3",
|
|
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",
|