@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 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
@@ -897,7 +897,7 @@ Built by **[Elite DCs, LLC](https://elitedcs.com)** — a digital marketing and
897
897
 
898
898
  **Tech stack:** TypeScript, Node.js, esbuild, MCP SDK, Zod, GHL API v2, Firebase Auth
899
899
 
900
- **Version:** 3.83.2
900
+ **Version:** 3.83.3
901
901
 
902
902
  ---
903
903
 
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 response = await fetch(`https://backend.leadconnectorhq.com/funnels/builder/autosave/${pageId}`, {
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({ key: "authentication", ok: authenticated, text: authenticated ? "The Anthropic key authenticates." : key ? "The Anthropic key did not authenticate. Paste a working key in Your agency and press Check and save this key." : "Paste your Anthropic key in Your agency and press Check and save this key." });
26976
- return { ok: lines.every((l) => l.ok), lines };
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.2",
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="Submits one test lead, then deletes it.";el.appendChild(disclosure)}
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: c6cd1ce6e416eaff -->
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, funnel, 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.
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, funnel, 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>
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.2",
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",