@elitedcs/ghl-mcp 3.83.2 → 3.83.4
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 +54 -0
- package/README.md +1 -1
- package/dist/index.js +141 -104
- package/guide/guide.html +4 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,60 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 3.83.4 — one door for every write, so "protected" cannot quietly stop meaning protected
|
|
6
|
+
|
|
7
|
+
3.83.3 fixed the holes. This closes the shape that kept making them.
|
|
8
|
+
|
|
9
|
+
Marking a client account protected is checked in one place for the public API. But
|
|
10
|
+
this product also writes to GoHighLevel through its own builder connection, and
|
|
11
|
+
eleven tool modules had each grown their own copy of that request — their own
|
|
12
|
+
headers, their own timeout, their own fetch. Every copy was a separate door into
|
|
13
|
+
your account, and a check added to one of them told you nothing about the other
|
|
14
|
+
ten. That is why the last release had to find fourteen of them.
|
|
15
|
+
|
|
16
|
+
They now share one door. It builds the headers, applies the thirty-second timeout
|
|
17
|
+
and runs the protected-account check once, for all of them. A tool written next
|
|
18
|
+
year gets the check without its author having to remember, and a test refuses any
|
|
19
|
+
new code that tries to go around it.
|
|
20
|
+
|
|
21
|
+
Nothing about what the tools do has changed. The same requests go to the same
|
|
22
|
+
places with the same bodies and the same headers, including the two different
|
|
23
|
+
builder origins GoHighLevel requires for page saves and for everything else. If
|
|
24
|
+
you cannot tell the difference from the outside, that is the intended result.
|
|
25
|
+
|
|
26
|
+
Also in this release: `update_form` now tells you what a field needs in order to
|
|
27
|
+
RENDER rather than only how to send one. A custom question needs the real custom
|
|
28
|
+
field's id, key and data type from your account; a field written without them is
|
|
29
|
+
stored and shows as raw data instead of a rendered field.
|
|
30
|
+
|
|
31
|
+
## 3.83.3 — an account you marked protected is now actually protected
|
|
32
|
+
|
|
33
|
+
**Marking a client account protected did not stop fourteen write paths from writing to it.**
|
|
34
|
+
The guard was real and it was in the right place for the public API, but this product also
|
|
35
|
+
writes to GoHighLevel through its own builder connection, and those calls never passed it.
|
|
36
|
+
So a build could create a pipeline and a form on an account you had explicitly told it never
|
|
37
|
+
to touch, and nothing would say so. Pipelines and forms are the first two things a build
|
|
38
|
+
creates. Every one of those paths now goes through the same check, including the template
|
|
39
|
+
installer, which writes a funnel and its pages — the case that matters most, because
|
|
40
|
+
GoHighLevel keeps only the first content save on a page.
|
|
41
|
+
|
|
42
|
+
**Verify no longer needs your own Anthropic key before it will let you start.** A new client
|
|
43
|
+
with no key met a disabled Run button, while the same screen said most people do not need to
|
|
44
|
+
paste anything there. Both were true at once: runs use whatever you are signed in to Claude
|
|
45
|
+
with and cost nothing extra, and the readiness check demanded a key anyway. The key is
|
|
46
|
+
reported now, not required. If you have one saved, runs bill it; if you do not, they use your
|
|
47
|
+
Claude sign-in.
|
|
48
|
+
|
|
49
|
+
**The Verify tile stopped promising something it could not do.** It said it submits a test
|
|
50
|
+
lead and deletes it. Since funnels left the build, no plan it creates has anywhere to send
|
|
51
|
+
one, and the report a screen later said so. It now says what it actually does for every plan,
|
|
52
|
+
and still discloses the test lead for the accounts that can take one.
|
|
53
|
+
|
|
54
|
+
**An older install can no longer roll your skills backwards.** The installer refreshed any
|
|
55
|
+
skill file it had written before, without checking whether the version doing the writing was
|
|
56
|
+
older than the one that wrote it. Running an older copy alongside a newer one quietly reverted
|
|
57
|
+
your skills. It refuses now and tells you it kept the newer ones.
|
|
58
|
+
|
|
5
59
|
## 3.83.2 — the static design-rule harness and its fixtures leave the package
|
|
6
60
|
|
|
7
61
|
A development harness and its four HTML fixtures were in the published package in
|
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1922,6 +1922,25 @@ function validateActionChain(actions, existingIds) {
|
|
|
1922
1922
|
}
|
|
1923
1923
|
}
|
|
1924
1924
|
}
|
|
1925
|
+
async function builderRequest(client, method, url, opts = {}) {
|
|
1926
|
+
assertWriteAllowed(method, url, { body: opts.body, defaultLocationId: client.locationId });
|
|
1927
|
+
const headers = { ...await client.buildHeaders(), ...opts.headers ?? {} };
|
|
1928
|
+
const init = { method, headers };
|
|
1929
|
+
if (opts.body !== void 0) init.body = JSON.stringify(opts.body);
|
|
1930
|
+
const controller = new AbortController();
|
|
1931
|
+
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
1932
|
+
init.signal = controller.signal;
|
|
1933
|
+
try {
|
|
1934
|
+
return await fetch(url, init);
|
|
1935
|
+
} catch (error) {
|
|
1936
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1937
|
+
throw new Error(`Request timeout (30s): ${method} ${url}`);
|
|
1938
|
+
}
|
|
1939
|
+
throw error;
|
|
1940
|
+
} finally {
|
|
1941
|
+
clearTimeout(timeout);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1925
1944
|
var fs4, path4, dotenv, import_zod4, BACKEND_BASE, FIREBASE_TOKEN_URL, MAX_RETRIES2, BASE_DELAY_MS2, FirebaseTokenSchema, WorkflowActionSchema, WorkflowFullSchema, CreateWorkflowResponseSchema, ParkedContactsError, UUID_V4, WorkflowBuilderClient;
|
|
1926
1945
|
var init_workflow_builder_client = __esm({
|
|
1927
1946
|
"src/workflow-builder-client.ts"() {
|
|
@@ -7435,13 +7454,8 @@ function registerEmailTools(server2, client) {
|
|
|
7435
7454
|
function registerEmailBuilderInternalTools(server2, builderClient, publicClient) {
|
|
7436
7455
|
if (!builderClient) return;
|
|
7437
7456
|
const client = builderClient;
|
|
7438
|
-
async function
|
|
7439
|
-
const
|
|
7440
|
-
const response = await fetch(`${EMAIL_BUILDER_BASE}${path39}`, {
|
|
7441
|
-
method,
|
|
7442
|
-
headers,
|
|
7443
|
-
body: body ? JSON.stringify(body) : void 0
|
|
7444
|
-
});
|
|
7457
|
+
async function builderRequest2(method, path39, body) {
|
|
7458
|
+
const response = await builderRequest(client, method, `${EMAIL_BUILDER_BASE}${path39}`, body ? { body } : {});
|
|
7445
7459
|
if (!response.ok) {
|
|
7446
7460
|
const text2 = await response.text();
|
|
7447
7461
|
throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${path39}
|
|
@@ -7463,7 +7477,7 @@ ${text2}`);
|
|
|
7463
7477
|
const loc = locationId2 ?? client.locationId;
|
|
7464
7478
|
return deleteEmailTemplateThroughDoors({
|
|
7465
7479
|
publicDelete: () => publicClient.delete(publicEmailTemplateDeletePath(loc, templateId)),
|
|
7466
|
-
internalDelete: () =>
|
|
7480
|
+
internalDelete: () => builderRequest2("DELETE", internalEmailTemplateDeletePath(templateId, loc))
|
|
7467
7481
|
});
|
|
7468
7482
|
}
|
|
7469
7483
|
);
|
|
@@ -7478,7 +7492,7 @@ ${text2}`);
|
|
|
7478
7492
|
},
|
|
7479
7493
|
async ({ templateId, name, locationId: locationId2 }) => {
|
|
7480
7494
|
const loc = locationId2 ?? client.locationId;
|
|
7481
|
-
return
|
|
7495
|
+
return builderRequest2("PATCH", `/${templateId}`, { locationId: loc, name });
|
|
7482
7496
|
}
|
|
7483
7497
|
);
|
|
7484
7498
|
safeTool(
|
|
@@ -7492,7 +7506,7 @@ ${text2}`);
|
|
|
7492
7506
|
},
|
|
7493
7507
|
async ({ templateId, archived, locationId: locationId2 }) => {
|
|
7494
7508
|
const loc = locationId2 ?? client.locationId;
|
|
7495
|
-
return
|
|
7509
|
+
return builderRequest2("PATCH", `/${templateId}`, { locationId: loc, archived: archived ?? true });
|
|
7496
7510
|
}
|
|
7497
7511
|
);
|
|
7498
7512
|
safeTool(
|
|
@@ -7507,11 +7521,8 @@ ${text2}`);
|
|
|
7507
7521
|
},
|
|
7508
7522
|
async ({ name, body, type, locationId: locationId2 }) => {
|
|
7509
7523
|
const loc = locationId2 ?? client.locationId;
|
|
7510
|
-
const
|
|
7511
|
-
|
|
7512
|
-
method: "POST",
|
|
7513
|
-
headers,
|
|
7514
|
-
body: JSON.stringify(buildSnippetPayload({ name, body, type }))
|
|
7524
|
+
const response = await builderRequest(client, "POST", `${SNIPPETS_BASE}/${loc}`, {
|
|
7525
|
+
body: buildSnippetPayload({ name, body, type })
|
|
7515
7526
|
});
|
|
7516
7527
|
if (!response.ok) {
|
|
7517
7528
|
const text2 = await response.text();
|
|
@@ -7541,6 +7552,7 @@ var init_emails = __esm({
|
|
|
7541
7552
|
init_account_read_retry();
|
|
7542
7553
|
init_account_reads();
|
|
7543
7554
|
import_zod28 = require("zod");
|
|
7555
|
+
init_workflow_builder_client();
|
|
7544
7556
|
init_tool_helpers();
|
|
7545
7557
|
EMAIL_BUILDER_BASE = "https://backend.leadconnectorhq.com/emails/builder";
|
|
7546
7558
|
SNIPPETS_BASE = "https://services.leadconnectorhq.com/snippets";
|
|
@@ -10289,12 +10301,15 @@ function readId(workflow) {
|
|
|
10289
10301
|
const id = workflow._id ?? record.id;
|
|
10290
10302
|
return typeof id === "string" ? id : "";
|
|
10291
10303
|
}
|
|
10304
|
+
function attributeBag(attributes) {
|
|
10305
|
+
return attributes ? Object.fromEntries(Object.entries(attributes)) : {};
|
|
10306
|
+
}
|
|
10292
10307
|
function summarizeWebhookWorkflow(workflow) {
|
|
10293
10308
|
const templates = workflow.workflowData?.templates ?? [];
|
|
10294
10309
|
const webhookActions = templates.filter((action) => action.type === "webhook");
|
|
10295
10310
|
if (!webhookActions.length) return null;
|
|
10296
10311
|
const actions = webhookActions.map((action) => {
|
|
10297
|
-
const attributes = action.attributes
|
|
10312
|
+
const attributes = attributeBag(action.attributes);
|
|
10298
10313
|
return {
|
|
10299
10314
|
actionId: action.id,
|
|
10300
10315
|
name: action.name,
|
|
@@ -10451,7 +10466,7 @@ function registerWebhookTools(server2, builderClient) {
|
|
|
10451
10466
|
}
|
|
10452
10467
|
const target = actionId ? webhookActions.find((a) => a.id === actionId) : webhookActions[0];
|
|
10453
10468
|
if (!target) throw new Error(`No webhook action with id ${actionId} on workflow ${webhookId}.`);
|
|
10454
|
-
const attributes = target.attributes
|
|
10469
|
+
const attributes = attributeBag(target.attributes);
|
|
10455
10470
|
const validated = buildWebhookAction({
|
|
10456
10471
|
id: target.id,
|
|
10457
10472
|
name: target.name,
|
|
@@ -11233,15 +11248,17 @@ function registerFunnelBuilderTools(server2, builderClient) {
|
|
|
11233
11248
|
return client.request("DELETE", path39);
|
|
11234
11249
|
}
|
|
11235
11250
|
async function funnelRequest(method, path39, body) {
|
|
11236
|
-
const headers = await client.buildHeaders();
|
|
11237
|
-
headers.Origin = "https://app.gohighlevel.com";
|
|
11238
|
-
headers.Referer = "https://app.gohighlevel.com/";
|
|
11239
11251
|
const url = `https://backend.leadconnectorhq.com/funnels${path39}`;
|
|
11240
|
-
const
|
|
11241
|
-
|
|
11242
|
-
|
|
11243
|
-
|
|
11244
|
-
|
|
11252
|
+
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
11253
|
+
const response = await builderRequest(client, method, url, {
|
|
11254
|
+
...sendsBody ? { body } : {},
|
|
11255
|
+
headers: {
|
|
11256
|
+
// Internal-API calls require the app origin (autosave needs the
|
|
11257
|
+
// page-builder one instead; see the autosave call).
|
|
11258
|
+
Origin: "https://app.gohighlevel.com",
|
|
11259
|
+
Referer: "https://app.gohighlevel.com/"
|
|
11260
|
+
}
|
|
11261
|
+
});
|
|
11245
11262
|
if (!response.ok) {
|
|
11246
11263
|
const text2 = await response.text();
|
|
11247
11264
|
throw new Error(`Funnel API Error ${response.status}: ${method} ${path39}
|
|
@@ -11503,11 +11520,17 @@ ${text2}`);
|
|
|
11503
11520
|
};
|
|
11504
11521
|
}
|
|
11505
11522
|
async function autosaveRequest(pageId, body) {
|
|
11506
|
-
const headers = await client.buildHeaders();
|
|
11507
|
-
headers.Origin = "https://page-builder.leadconnectorhq.com";
|
|
11508
|
-
headers.Referer = "https://page-builder.leadconnectorhq.com/";
|
|
11509
11523
|
const url = `https://backend.leadconnectorhq.com/funnels/builder/autosave/${pageId}`;
|
|
11510
|
-
const response = await
|
|
11524
|
+
const response = await builderRequest(client, "POST", url, {
|
|
11525
|
+
body,
|
|
11526
|
+
headers: {
|
|
11527
|
+
// The autosave endpoint's IAM check requires the builder origin (verified
|
|
11528
|
+
// live — app.gohighlevel.com was not tested and page-builder is what the
|
|
11529
|
+
// real builder sends).
|
|
11530
|
+
Origin: "https://page-builder.leadconnectorhq.com",
|
|
11531
|
+
Referer: "https://page-builder.leadconnectorhq.com/"
|
|
11532
|
+
}
|
|
11533
|
+
});
|
|
11511
11534
|
if (!response.ok) {
|
|
11512
11535
|
throw new Error(`Funnel API Error ${response.status}: POST /builder/autosave/${pageId}
|
|
11513
11536
|
${await response.text()}`);
|
|
@@ -11613,6 +11636,7 @@ var init_funnel_builder = __esm({
|
|
|
11613
11636
|
"src/tools/funnel-builder.ts"() {
|
|
11614
11637
|
"use strict";
|
|
11615
11638
|
import_zod41 = require("zod");
|
|
11639
|
+
init_workflow_builder_client();
|
|
11616
11640
|
init_tool_helpers();
|
|
11617
11641
|
init_page_reads();
|
|
11618
11642
|
}
|
|
@@ -11815,6 +11839,9 @@ function requireCompany(api) {
|
|
|
11815
11839
|
}
|
|
11816
11840
|
async function call(api, path39, init) {
|
|
11817
11841
|
const headers = { ...await api.headers(), Origin: "https://app.gohighlevel.com", Referer: "https://app.gohighlevel.com/" };
|
|
11842
|
+
if (MUTATING_PATHS.some((mutating) => path39.startsWith(mutating))) {
|
|
11843
|
+
assertWriteAllowed("POST", SVC + path39, { defaultLocationId: api.locationId() });
|
|
11844
|
+
}
|
|
11818
11845
|
const doFetch = api.fetch ?? fetch;
|
|
11819
11846
|
const response = await doFetch(SVC + path39, { ...init, headers });
|
|
11820
11847
|
const text = await response.text();
|
|
@@ -11908,12 +11935,14 @@ function rankForBrand(rows, brand) {
|
|
|
11908
11935
|
return { ...t, why: parts.join("; ") };
|
|
11909
11936
|
});
|
|
11910
11937
|
}
|
|
11911
|
-
var SVC, GHL_ID;
|
|
11938
|
+
var SVC, MUTATING_PATHS, GHL_ID;
|
|
11912
11939
|
var init_ghl_templates = __esm({
|
|
11913
11940
|
"src/command-os/design/ghl-templates.ts"() {
|
|
11914
11941
|
"use strict";
|
|
11915
11942
|
init_color();
|
|
11943
|
+
init_account_rules();
|
|
11916
11944
|
SVC = "https://services.leadconnectorhq.com";
|
|
11945
|
+
MUTATING_PATHS = ["/templates/template/load"];
|
|
11917
11946
|
GHL_ID = /^[A-Za-z0-9]{15,40}$/;
|
|
11918
11947
|
}
|
|
11919
11948
|
});
|
|
@@ -16465,10 +16494,15 @@ function registerPageStudioTools(server2, builderClient) {
|
|
|
16465
16494
|
return { map: out, truncated };
|
|
16466
16495
|
}
|
|
16467
16496
|
async function funnelRequest(method, path39, body) {
|
|
16468
|
-
const
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
16497
|
+
const response = await builderRequest(client, method, `https://backend.leadconnectorhq.com/funnels${path39}`, {
|
|
16498
|
+
...body ? { body } : {},
|
|
16499
|
+
headers: {
|
|
16500
|
+
// Internal-API calls require the app origin (autosave needs the
|
|
16501
|
+
// page-builder one instead; see the autosave call).
|
|
16502
|
+
Origin: "https://app.gohighlevel.com",
|
|
16503
|
+
Referer: "https://app.gohighlevel.com/"
|
|
16504
|
+
}
|
|
16505
|
+
});
|
|
16472
16506
|
if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${path39}
|
|
16473
16507
|
${await response.text()}`);
|
|
16474
16508
|
const text = await response.text();
|
|
@@ -16485,13 +16519,13 @@ ${await response.text()}`);
|
|
|
16485
16519
|
}
|
|
16486
16520
|
}
|
|
16487
16521
|
async function autosaveRequest(pageId, body) {
|
|
16488
|
-
const
|
|
16489
|
-
|
|
16490
|
-
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16494
|
-
|
|
16522
|
+
const autosaveUrl = `https://backend.leadconnectorhq.com/funnels/builder/autosave/${pageId}`;
|
|
16523
|
+
const response = await builderRequest(client, "POST", autosaveUrl, {
|
|
16524
|
+
body,
|
|
16525
|
+
headers: {
|
|
16526
|
+
Origin: "https://page-builder.leadconnectorhq.com",
|
|
16527
|
+
Referer: "https://page-builder.leadconnectorhq.com/"
|
|
16528
|
+
}
|
|
16495
16529
|
});
|
|
16496
16530
|
if (!response.ok) throw new Error(`Funnel API Error ${response.status}: POST /builder/autosave/${pageId}
|
|
16497
16531
|
${await response.text()}`);
|
|
@@ -16977,6 +17011,7 @@ var init_page_studio = __esm({
|
|
|
16977
17011
|
"use strict";
|
|
16978
17012
|
import_zod43 = require("zod");
|
|
16979
17013
|
crypto4 = __toESM(require("node:crypto"));
|
|
17014
|
+
init_workflow_builder_client();
|
|
16980
17015
|
init_ghl_templates();
|
|
16981
17016
|
init_retheme();
|
|
16982
17017
|
init_ghl_page();
|
|
@@ -17073,13 +17108,9 @@ function cleanFormDataForSave(formData) {
|
|
|
17073
17108
|
return isJsonObject(cleaned) ? cleaned : formData;
|
|
17074
17109
|
}
|
|
17075
17110
|
async function formApiRequest(client, method, path39, body) {
|
|
17076
|
-
const headers = await client.buildHeaders();
|
|
17077
17111
|
const url = `https://backend.leadconnectorhq.com/forms${path39}`;
|
|
17078
|
-
const
|
|
17079
|
-
|
|
17080
|
-
options.body = JSON.stringify(body);
|
|
17081
|
-
}
|
|
17082
|
-
const response = await fetch(url, options);
|
|
17112
|
+
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
17113
|
+
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
17083
17114
|
if (!response.ok) {
|
|
17084
17115
|
const text2 = await response.text();
|
|
17085
17116
|
throw new Error(`Form API Error ${response.status}: ${method} ${path39}
|
|
@@ -17146,10 +17177,10 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
|
|
|
17146
17177
|
);
|
|
17147
17178
|
server2.tool(
|
|
17148
17179
|
"update_form",
|
|
17149
|
-
"Update a form's structure \u2014 fields, labels, conditional logic, auto-responder, email notifications, styling. Use get_form_full first to read the current form, modify the formData object, then pass it here. If name is omitted, the current name is preserved (one extra GET).",
|
|
17180
|
+
"Update a form's structure \u2014 fields, labels, conditional logic, auto-responder, email notifications, styling. Use get_form_full first to read the current form, modify the formData object, then pass it here. If name is omitted, the current name is preserved (one extra GET). IMPORTANT \u2014 a NEW field will not render in GoHighLevel's form editor unless it carries the binding GHL needs: a custom-question field must include the real custom field's `id`, `fieldKey` and `dataType` from THIS account (read them with get_custom_fields first; do not invent them), and a standard field (first_name, last_name, email, phone) needs `standard: true` with its `tag`. A field written without that binding is stored but shows as raw data instead of a rendered field. These shapes were reverse-engineered from live UI-built forms; copy an existing field from get_form_full rather than composing one from memory.",
|
|
17150
17181
|
{
|
|
17151
17182
|
formId: import_zod44.z.string().describe("The form ID to update."),
|
|
17152
|
-
formData: import_zod44.z.record(import_zod44.z.string(), import_zod44.z.unknown()).describe("The updated formData object containing form fields, settings, autoResponder config, etc."),
|
|
17183
|
+
formData: import_zod44.z.record(import_zod44.z.string(), import_zod44.z.unknown()).describe("The updated formData object containing form fields, settings, autoResponder config, etc. Fields live at formData.form.fields; each one needs the binding described above or the editor cannot render it."),
|
|
17153
17184
|
name: import_zod44.z.string().optional().describe("Updated form name. If omitted, the current name is preserved.")
|
|
17154
17185
|
},
|
|
17155
17186
|
async ({ formId, formData, name }) => {
|
|
@@ -17276,6 +17307,7 @@ var init_form_builder = __esm({
|
|
|
17276
17307
|
"src/tools/form-builder.ts"() {
|
|
17277
17308
|
"use strict";
|
|
17278
17309
|
import_zod44 = require("zod");
|
|
17310
|
+
init_workflow_builder_client();
|
|
17279
17311
|
init_tool_helpers();
|
|
17280
17312
|
}
|
|
17281
17313
|
});
|
|
@@ -17669,13 +17701,9 @@ function registerPipelineBuilderTools(server2, builderClient) {
|
|
|
17669
17701
|
if (!builderClient) return;
|
|
17670
17702
|
const client = builderClient;
|
|
17671
17703
|
async function pipelineRequest(method, path39, body) {
|
|
17672
|
-
const headers = await client.buildHeaders();
|
|
17673
17704
|
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path39}`;
|
|
17674
|
-
const
|
|
17675
|
-
|
|
17676
|
-
options.body = JSON.stringify(body);
|
|
17677
|
-
}
|
|
17678
|
-
const response = await fetch(url, options);
|
|
17705
|
+
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT" || method === "PATCH");
|
|
17706
|
+
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
17679
17707
|
if (!response.ok) {
|
|
17680
17708
|
const text2 = await response.text();
|
|
17681
17709
|
throw new Error(`Pipeline API Error ${response.status}: ${method} ${path39}
|
|
@@ -17853,6 +17881,7 @@ var init_pipeline_builder = __esm({
|
|
|
17853
17881
|
"src/tools/pipeline-builder.ts"() {
|
|
17854
17882
|
"use strict";
|
|
17855
17883
|
import_zod46 = require("zod");
|
|
17884
|
+
init_workflow_builder_client();
|
|
17856
17885
|
}
|
|
17857
17886
|
});
|
|
17858
17887
|
|
|
@@ -26972,8 +27001,13 @@ async function clientPreflight(registry2, locationId2) {
|
|
|
26972
27001
|
}
|
|
26973
27002
|
} catch {
|
|
26974
27003
|
}
|
|
26975
|
-
lines.push({
|
|
26976
|
-
|
|
27004
|
+
lines.push({
|
|
27005
|
+
key: "authentication",
|
|
27006
|
+
ok: authenticated,
|
|
27007
|
+
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."
|
|
27008
|
+
});
|
|
27009
|
+
const blocking = lines.filter((l) => l.key !== "authentication");
|
|
27010
|
+
return { ok: blocking.every((l) => l.ok), lines };
|
|
26977
27011
|
}
|
|
26978
27012
|
async function agencyConnectionReady(registry2, companyId) {
|
|
26979
27013
|
const key = registry2.getAgencyKey();
|
|
@@ -28557,7 +28591,7 @@ var require_package = __commonJS({
|
|
|
28557
28591
|
"package.json"(exports2, module2) {
|
|
28558
28592
|
module2.exports = {
|
|
28559
28593
|
name: "@elitedcs/ghl-mcp",
|
|
28560
|
-
version: "3.83.
|
|
28594
|
+
version: "3.83.4",
|
|
28561
28595
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
28562
28596
|
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
28597
|
main: "dist/index.js",
|
|
@@ -35206,7 +35240,7 @@ function card(c,state,run){const st=(state.clients[c.locationId]||{stages:STAGES
|
|
|
35206
35240
|
el.appendChild(box)}
|
|
35207
35241
|
// One short line here; the button's confirmation carries the full wording. At 135
|
|
35208
35242
|
// 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="
|
|
35243
|
+
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
35244
|
if(gate)el.title=gate;
|
|
35211
35245
|
if(stale&&!live){const reason=document.createElement("small");reason.textContent=stale.reason;el.appendChild(reason)}
|
|
35212
35246
|
const oc=(state.outcomes||{})[c.locationId+":"+i];
|
|
@@ -39192,13 +39226,9 @@ function registerSmartListTools(server2, builderClient) {
|
|
|
39192
39226
|
if (!builderClient) return;
|
|
39193
39227
|
const client = builderClient;
|
|
39194
39228
|
async function smartListRequest(method, path39, body) {
|
|
39195
|
-
const headers = await client.buildHeaders();
|
|
39196
39229
|
const url = `${SMARTLIST_BASE}${path39}`;
|
|
39197
|
-
const
|
|
39198
|
-
|
|
39199
|
-
options.body = JSON.stringify(body);
|
|
39200
|
-
}
|
|
39201
|
-
const response = await fetch(url, options);
|
|
39230
|
+
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
39231
|
+
const response = await builderRequest(client, method, url, sendsBody ? { body } : {});
|
|
39202
39232
|
if (!response.ok) {
|
|
39203
39233
|
const text2 = await response.text();
|
|
39204
39234
|
throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path39}
|
|
@@ -39263,7 +39293,7 @@ ${await res.text()}`);
|
|
|
39263
39293
|
];
|
|
39264
39294
|
const body = buildContactSmartListBody({ listName: name, locationId: loc, filters });
|
|
39265
39295
|
const headers = await client.buildHeaders();
|
|
39266
|
-
const res = await
|
|
39296
|
+
const res = await builderRequest(client, "POST", CONTACT_SMARTLIST_BASE, { body });
|
|
39267
39297
|
if (!res.ok) throw new Error(`Smart Lists API Error ${res.status}: POST /contacts/smartlist
|
|
39268
39298
|
${await res.text()}`);
|
|
39269
39299
|
return jsonResponse(JSON.parse(await res.text() || "{}"));
|
|
@@ -39300,11 +39330,7 @@ ${await res.text()}`);
|
|
|
39300
39330
|
async ({ listId }) => {
|
|
39301
39331
|
try {
|
|
39302
39332
|
const headers = await client.buildHeaders();
|
|
39303
|
-
const res = await
|
|
39304
|
-
method: "POST",
|
|
39305
|
-
headers,
|
|
39306
|
-
body: JSON.stringify({ smartlist_id: listId })
|
|
39307
|
-
});
|
|
39333
|
+
const res = await builderRequest(client, "POST", CONTACT_SMARTLIST_DELETE, { body: { smartlist_id: listId } });
|
|
39308
39334
|
if (!res.ok) throw new Error(`Smart Lists API Error ${res.status}: POST /smartlist/delete
|
|
39309
39335
|
${await res.text()}`);
|
|
39310
39336
|
return jsonResponse(JSON.parse(await res.text() || "{}"));
|
|
@@ -39412,6 +39438,7 @@ var init_smart_lists = __esm({
|
|
|
39412
39438
|
"src/tools/smart-lists.ts"() {
|
|
39413
39439
|
"use strict";
|
|
39414
39440
|
import_zod57 = require("zod");
|
|
39441
|
+
init_workflow_builder_client();
|
|
39415
39442
|
init_tool_helpers();
|
|
39416
39443
|
SMARTLIST_BASE = "https://backend.leadconnectorhq.com/lists/dynamic";
|
|
39417
39444
|
OBJECT_KEYS = ["contacts", "opportunity"];
|
|
@@ -39434,8 +39461,7 @@ function registerReputationTools(server2, builderClient) {
|
|
|
39434
39461
|
if (!builderClient) return;
|
|
39435
39462
|
const client = builderClient;
|
|
39436
39463
|
async function reputationRequest(method, path39) {
|
|
39437
|
-
const
|
|
39438
|
-
const response = await fetch(`${REPUTATION_BASE}${path39}`, { method, headers });
|
|
39464
|
+
const response = await builderRequest(client, method, `${REPUTATION_BASE}${path39}`);
|
|
39439
39465
|
if (!response.ok) {
|
|
39440
39466
|
const text2 = await response.text();
|
|
39441
39467
|
throw new Error(`Reputation API Error ${response.status}: ${method} ${path39}
|
|
@@ -39511,6 +39537,7 @@ var init_reputation = __esm({
|
|
|
39511
39537
|
"src/tools/reputation.ts"() {
|
|
39512
39538
|
"use strict";
|
|
39513
39539
|
import_zod58 = require("zod");
|
|
39540
|
+
init_workflow_builder_client();
|
|
39514
39541
|
init_tool_helpers();
|
|
39515
39542
|
REPUTATION_BASE = "https://backend.leadconnectorhq.com/reputation";
|
|
39516
39543
|
}
|
|
@@ -39545,12 +39572,7 @@ function registerEmailCampaignTools(server2, builderClient) {
|
|
|
39545
39572
|
if (isPlainText !== void 0) body.isPlainText = isPlainText;
|
|
39546
39573
|
if (enableResendToUnopened !== void 0) body.enableResendToUnopened = enableResendToUnopened;
|
|
39547
39574
|
if (hasUtmTracking !== void 0) body.hasUtmTracking = hasUtmTracking;
|
|
39548
|
-
const
|
|
39549
|
-
const response = await fetch(`${SVC_BASE}/emails/schedule`, {
|
|
39550
|
-
method: "POST",
|
|
39551
|
-
headers,
|
|
39552
|
-
body: JSON.stringify(body)
|
|
39553
|
-
});
|
|
39575
|
+
const response = await builderRequest(client, "POST", `${SVC_BASE}/emails/schedule`, { body });
|
|
39554
39576
|
if (!response.ok) {
|
|
39555
39577
|
const text2 = await response.text();
|
|
39556
39578
|
throw new Error(`Email Campaign API Error ${response.status}: POST /emails/schedule
|
|
@@ -39570,6 +39592,7 @@ var init_email_campaigns = __esm({
|
|
|
39570
39592
|
"src/tools/email-campaigns.ts"() {
|
|
39571
39593
|
"use strict";
|
|
39572
39594
|
import_zod59 = require("zod");
|
|
39595
|
+
init_workflow_builder_client();
|
|
39573
39596
|
init_tool_helpers();
|
|
39574
39597
|
SVC_BASE = "https://services.leadconnectorhq.com";
|
|
39575
39598
|
}
|
|
@@ -39580,12 +39603,7 @@ function registerMembershipTools(server2, builderClient) {
|
|
|
39580
39603
|
if (!builderClient) return;
|
|
39581
39604
|
const client = builderClient;
|
|
39582
39605
|
async function membershipRequest(path39, method = "GET", body) {
|
|
39583
|
-
const
|
|
39584
|
-
const response = await fetch(`${MEMBERSHIP_BASE2}${path39}`, {
|
|
39585
|
-
method,
|
|
39586
|
-
headers,
|
|
39587
|
-
body: body ? JSON.stringify(body) : void 0
|
|
39588
|
-
});
|
|
39606
|
+
const response = await builderRequest(client, method, `${MEMBERSHIP_BASE2}${path39}`, body ? { body } : {});
|
|
39589
39607
|
if (!response.ok) {
|
|
39590
39608
|
const text2 = await response.text();
|
|
39591
39609
|
throw new Error(`Membership API Error ${response.status}: ${method} ${path39}
|
|
@@ -39766,6 +39784,7 @@ var init_memberships = __esm({
|
|
|
39766
39784
|
"src/tools/memberships.ts"() {
|
|
39767
39785
|
"use strict";
|
|
39768
39786
|
import_zod60 = require("zod");
|
|
39787
|
+
init_workflow_builder_client();
|
|
39769
39788
|
init_tool_helpers();
|
|
39770
39789
|
MEMBERSHIP_BASE2 = "https://backend.leadconnectorhq.com/membership";
|
|
39771
39790
|
}
|
|
@@ -40114,11 +40133,8 @@ Pipelines: ${template.pipelines.length} to create`);
|
|
|
40114
40133
|
}
|
|
40115
40134
|
} else {
|
|
40116
40135
|
try {
|
|
40117
|
-
const
|
|
40118
|
-
|
|
40119
|
-
method: "POST",
|
|
40120
|
-
headers,
|
|
40121
|
-
body: JSON.stringify({
|
|
40136
|
+
const res = await builderRequest(builderClient, "POST", "https://backend.leadconnectorhq.com/opportunities/pipelines", {
|
|
40137
|
+
body: {
|
|
40122
40138
|
name: resolved2.name,
|
|
40123
40139
|
stages: (resolved2.stages || []).map((s) => ({
|
|
40124
40140
|
...s,
|
|
@@ -40128,7 +40144,7 @@ Pipelines: ${template.pipelines.length} to create`);
|
|
|
40128
40144
|
locationId: locId,
|
|
40129
40145
|
showInFunnel: true,
|
|
40130
40146
|
showInPieChart: true
|
|
40131
|
-
}
|
|
40147
|
+
}
|
|
40132
40148
|
});
|
|
40133
40149
|
if (res.ok) {
|
|
40134
40150
|
counts.pipelines.created++;
|
|
@@ -40289,6 +40305,7 @@ var init_template_deployer = __esm({
|
|
|
40289
40305
|
init_tool_helpers();
|
|
40290
40306
|
fs28 = __toESM(require("fs"));
|
|
40291
40307
|
path27 = __toESM(require("path"));
|
|
40308
|
+
init_workflow_builder_client();
|
|
40292
40309
|
TemplateSchema = import_zod61.z.object({
|
|
40293
40310
|
templateName: import_zod61.z.string(),
|
|
40294
40311
|
templateVersion: import_zod61.z.string().optional(),
|
|
@@ -50950,8 +50967,7 @@ function pickCalendarPins(workflow) {
|
|
|
50950
50967
|
}
|
|
50951
50968
|
function makeFirebaseRequest(builderClient) {
|
|
50952
50969
|
return async (url, method, body) => {
|
|
50953
|
-
const
|
|
50954
|
-
const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : void 0 });
|
|
50970
|
+
const response = await builderRequest(builderClient, method, url, body ? { body } : {});
|
|
50955
50971
|
if (!response.ok) {
|
|
50956
50972
|
const text2 = await response.text();
|
|
50957
50973
|
throw new Error(`API ${response.status}: ${method} ${url.replace(/^https:\/\/[^/]+/, "")}
|
|
@@ -50963,11 +50979,9 @@ ${text2}`);
|
|
|
50963
50979
|
}
|
|
50964
50980
|
function makePipelineApi(builderClient) {
|
|
50965
50981
|
return async (method, path39, body) => {
|
|
50966
|
-
const headers = await builderClient.buildHeaders();
|
|
50967
50982
|
const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path39}`;
|
|
50968
|
-
const
|
|
50969
|
-
|
|
50970
|
-
const response = await fetch(url, options);
|
|
50983
|
+
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
50984
|
+
const response = await builderRequest(builderClient, method, url, sendsBody ? { body } : {});
|
|
50971
50985
|
if (!response.ok) {
|
|
50972
50986
|
const text2 = await response.text();
|
|
50973
50987
|
throw new Error(`Pipeline API ${response.status}: ${method} ${path39}
|
|
@@ -50984,13 +50998,17 @@ ${text2}`);
|
|
|
50984
50998
|
}
|
|
50985
50999
|
function makeFunnelApi(builderClient) {
|
|
50986
51000
|
return async (method, path39, body) => {
|
|
50987
|
-
const headers = await builderClient.buildHeaders();
|
|
50988
|
-
headers.Origin = "https://app.gohighlevel.com";
|
|
50989
|
-
headers.Referer = "https://app.gohighlevel.com/";
|
|
50990
51001
|
const url = `https://backend.leadconnectorhq.com/funnels${path39}`;
|
|
50991
|
-
const
|
|
50992
|
-
|
|
50993
|
-
|
|
51002
|
+
const sendsBody = body !== void 0 && (method === "POST" || method === "PUT");
|
|
51003
|
+
const response = await builderRequest(builderClient, method, url, {
|
|
51004
|
+
...sendsBody ? { body } : {},
|
|
51005
|
+
headers: {
|
|
51006
|
+
// Origin/Referer REQUIRED on internal-API funnel writes (else 401
|
|
51007
|
+
// "Error calling IAM service") — verified in funnel-builder.ts.
|
|
51008
|
+
Origin: "https://app.gohighlevel.com",
|
|
51009
|
+
Referer: "https://app.gohighlevel.com/"
|
|
51010
|
+
}
|
|
51011
|
+
});
|
|
50994
51012
|
if (!response.ok) {
|
|
50995
51013
|
const text2 = await response.text();
|
|
50996
51014
|
throw new Error(`Funnel API ${response.status}: ${method} ${path39}
|
|
@@ -52101,6 +52119,7 @@ var init_intake_to_build = __esm({
|
|
|
52101
52119
|
init_build_contract();
|
|
52102
52120
|
init_build_obligations();
|
|
52103
52121
|
import_zod80 = require("zod");
|
|
52122
|
+
init_workflow_builder_client();
|
|
52104
52123
|
init_tool_helpers();
|
|
52105
52124
|
init_form_builder();
|
|
52106
52125
|
init_calendars();
|
|
@@ -52658,6 +52677,15 @@ var os6 = __toESM(require("node:os"));
|
|
|
52658
52677
|
function sha2562(buf) {
|
|
52659
52678
|
return (0, import_node_crypto12.createHash)("sha256").update(buf).digest("hex");
|
|
52660
52679
|
}
|
|
52680
|
+
function isOlderRelease(candidate, installed) {
|
|
52681
|
+
const parts = (v) => v.trim().split(".").map((n) => Number.parseInt(n, 10));
|
|
52682
|
+
const a = parts(candidate), b = parts(installed);
|
|
52683
|
+
if (a.length !== 3 || b.length !== 3 || [...a, ...b].some(Number.isNaN)) return false;
|
|
52684
|
+
for (let i = 0; i < 3; i++) {
|
|
52685
|
+
if (a[i] !== b[i]) return a[i] < b[i];
|
|
52686
|
+
}
|
|
52687
|
+
return false;
|
|
52688
|
+
}
|
|
52661
52689
|
function bundledSkillsDir(baseDir) {
|
|
52662
52690
|
const candidate = path37.resolve(baseDir, "..", "skills");
|
|
52663
52691
|
try {
|
|
@@ -52691,6 +52719,7 @@ function installBundledSkills(opts) {
|
|
|
52691
52719
|
installed: [],
|
|
52692
52720
|
updated: [],
|
|
52693
52721
|
skippedUserModified: [],
|
|
52722
|
+
skippedOlderPackage: [],
|
|
52694
52723
|
unchanged: [],
|
|
52695
52724
|
errors: [],
|
|
52696
52725
|
targetDir: opts.targetDir ?? path37.join(os6.homedir(), ".claude", "skills"),
|
|
@@ -52733,7 +52762,11 @@ function installBundledSkills(opts) {
|
|
|
52733
52762
|
continue;
|
|
52734
52763
|
}
|
|
52735
52764
|
const lastWritten = marker.files[rel];
|
|
52736
|
-
if (lastWritten && destHash === lastWritten) {
|
|
52765
|
+
if (lastWritten && destHash === lastWritten && isOlderRelease(opts.packageVersion, marker.packageVersion)) {
|
|
52766
|
+
newMarker.files[rel] = destHash;
|
|
52767
|
+
newMarker.packageVersion = marker.packageVersion;
|
|
52768
|
+
result.skippedOlderPackage.push(rel);
|
|
52769
|
+
} else if (lastWritten && destHash === lastWritten) {
|
|
52737
52770
|
fs38.writeFileSync(dest, src);
|
|
52738
52771
|
newMarker.files[rel] = srcHash;
|
|
52739
52772
|
result.updated.push(rel);
|
|
@@ -52757,6 +52790,7 @@ function summarizeInstall(r) {
|
|
|
52757
52790
|
if (r.installed.length) bits.push(`${r.installed.length} installed`);
|
|
52758
52791
|
if (r.updated.length) bits.push(`${r.updated.length} updated`);
|
|
52759
52792
|
if (r.skippedUserModified.length) bits.push(`${r.skippedUserModified.length} kept (your edits)`);
|
|
52793
|
+
if (r.skippedOlderPackage.length) bits.push(`${r.skippedOlderPackage.length} kept (newer than this version)`);
|
|
52760
52794
|
if (r.errors.length) bits.push(`${r.errors.length} errors`);
|
|
52761
52795
|
if (bits.length === 0) return "";
|
|
52762
52796
|
return `Skills: ${bits.join(", ")} \u2192 ${r.targetDir}`;
|
|
@@ -52786,6 +52820,9 @@ function registerSkillsTool(server2, packageVersion, baseDir) {
|
|
|
52786
52820
|
installed: result.installed,
|
|
52787
52821
|
updated: result.updated,
|
|
52788
52822
|
keptYourEdits: result.skippedUserModified,
|
|
52823
|
+
// A member running an older install alongside a newer one would otherwise
|
|
52824
|
+
// have their skills rolled back without a word. BUG-2026-09-10-01.
|
|
52825
|
+
keptNewerThanThisVersion: result.skippedOlderPackage,
|
|
52789
52826
|
unchanged: result.unchanged.length,
|
|
52790
52827
|
errors: result.errors,
|
|
52791
52828
|
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.4",
|
|
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",
|