@elitedcs/ghl-mcp 3.6.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.7.0 — Funnel-builder fixes (Don Harris audit)
4
+
5
+ **178 tools across 38 modules. Bundle: 288.4 KB.**
6
+
7
+ Implements all 7 fixes from Don Harris's 2026-05-14 audit. Don captured the correct endpoints via Chrome DevTools against `page-builder.leadconnectorhq.com` and `app.gohighlevel.com`, then verified each fix with curl round-trips against two live sub-accounts. Full credit to Don for the diagnostic work.
8
+
9
+ ### The root cause
10
+ All funnel write tools were hitting endpoints that don't exist on `backend.leadconnectorhq.com/funnels/...`. The real save paths live on the same host but with different verbs, methods, and body shapes. Plus, mutating endpoints require `Origin` + `Referer` headers that `funnelRequest()` wasn't sending, causing 401 `{"message":"Error calling IAM service"}` on every write.
11
+
12
+ ### Header fix (unblocks every funnel write)
13
+ Added `Origin: https://app.gohighlevel.com` and `Referer: https://app.gohighlevel.com/` to `funnelRequest()`. Without these, GHL's IAM rejects writes regardless of bearer token validity.
14
+
15
+ ### Endpoint fixes
16
+ | Tool | Was | Now |
17
+ |---|---|---|
18
+ | `update_page_content` | `PUT /page/{pageId}` | `POST /builder/prebuilt-section/sync/changes` with `{ locationId, pageId, pageData, write: true, isPublished }` |
19
+ | `update_funnel` | `PUT /funnel/{funnelId}` | `POST /funnel/update-settings` with full settings body. Rewritten with fetch-then-merge so you can change one field without blowing away the others. |
20
+ | `create_funnel_page` | `POST /page` | `POST /funnel/create-step` with client-generated step UUID + nested `step` object. Now also returns the generated `stepId` so the caller can use it with `update_funnel_step` / `delete_funnel_page` without a separate lookup. |
21
+ | `delete_funnel_page` | `DELETE /page/{pageId}` | `POST /funnel/delete-step` with `{ funnelId, stepId }`. **SIGNATURE CHANGED**: now takes `funnelId + stepId` instead of `pageId`. |
22
+ | `delete_funnel` | `DELETE /funnel/{id}` | `POST /funnel/delete` with `{ funnelId, locationId, userId }` |
23
+
24
+ ### `update_funnel_step` (NEW)
25
+ Per-step renames, URL slugs, and domain attachments. `PUT /funnel/step/{funnelId}` with `{ stepId, name, url, domainName }`. Use this when you want to rename a single page entry inside a funnel without touching funnel-level settings.
26
+
27
+ ### `WorkflowBuilderClient.getUserId()` (NEW, internal)
28
+ Public getter on the builder client. Required because `delete_funnel` needs `userId` in the request body. Not exposed via MCP.
29
+
30
+ ### Round-trip verified in MCP Testing
31
+ - 6/6 tested funnel writes return success against real GHL backend (the 7th, `update_page_content`, was already verified by Don's 112KB real-page round-trip in his audit email).
32
+ - Created → renamed → added step → renamed step → deleted step → deleted funnel, all in one test pass.
33
+
34
+ ### Honest scoping
35
+ - The 168/178 "without Firebase / with Firebase" split documented in `setup_ghl_mcp` and `enable_workflow_builder` is carried forward from v3.5.1 and hasn't been audited empirically. It's likely off in absolute terms (funnel/form/pipeline builders all gate on Firebase too). TODO to do a real audit.
36
+ - The bonus `update_workflow_actions` / `remove_from_workflow` read/write asymmetry Don flagged at the end of his email is not addressed in this release — separate concern, separate fix when prioritized.
37
+
38
+ ### Files changed
39
+ - `src/tools/funnel-builder.ts` — full rewrite of all 6 broken endpoints + `update_funnel_step` added
40
+ - `src/workflow-builder-client.ts` — `getUserId()` public getter
41
+ - `src/setup-tool.ts` — tool counts refreshed
42
+
3
43
  ## 3.6.0 — `build_goal_event` (closes Don-e's last open complaint)
4
44
 
5
45
  **177 tools across 38 modules. Bundle: 281.9 KB.**
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GHL Command — GoHighLevel MCP Server
2
2
 
3
- **Full GoHighLevel API access for Claude.** 177 tools across 36 modules — manage contacts, conversations, pipelines, calendars, funnels, workflows, invoices, custom objects, webhooks, and more. **Includes full workflow builder, funnel/page editor, form builder, pipeline builder, bulk operations, account export, and workflow cloning** — capabilities no other GHL tool offers.
3
+ **Full GoHighLevel API access for Claude.** 178 tools across 36 modules — manage contacts, conversations, pipelines, calendars, funnels, workflows, invoices, custom objects, webhooks, and more. **Includes full workflow builder, funnel/page editor, form builder, pipeline builder, bulk operations, account export, and workflow cloning** — capabilities no other GHL tool offers.
4
4
 
5
5
  **Distributed via npm as [`@elitedcs/ghl-mcp`](https://www.npmjs.com/package/@elitedcs/ghl-mcp).** Buyers install with one config block — no git, no Node.js setup, no terminal commands. Updates flow automatically (`npx @latest` re-resolves on every Claude restart).
6
6
 
@@ -167,9 +167,9 @@ Re-run setup_ghl_mcp with workflow builder:
167
167
 
168
168
  ---
169
169
 
170
- ## Tools (177)
170
+ ## Tools (178)
171
171
 
172
- > **v3.6.0 adds `build_goal_event`** a high-level helper that emits a correctly-shaped GHL workflow goal-event node. Goal events sit inline in the action chain and terminate (or jump) the workflow when their condition fires. Closes Don-e's last open complaint from May 4 Claude can now both READ and BUILD goal events end-to-end.
172
+ > **v3.7.0 fixes Don Harris's 2026-05-14 funnel-builder audit.** All six previously-broken funnel write tools (`update_funnel`, `create_funnel_page`, `delete_funnel_page`, `delete_funnel`, `update_page_content`, plus `create_funnel` which 401'd) now hit the correct GHL endpoints. New: `update_funnel_step` for per-step renames / slugs / domains. Origin + Referer headers added to all funnel writes required by GHL's IAM, missing before.
173
173
 
174
174
  ### CRM & Contacts (15 tools)
175
175
 
@@ -542,7 +542,7 @@ ghl-command-mcp/
542
542
  │ ├── emails.ts # 1 tool — email campaign listing
543
543
  │ ├── trigger-links.ts # 1 tool — trigger link listing
544
544
  │ ├── workflow-builder.ts # 6 tools — full workflow CRUD (internal API)
545
- │ ├── funnel-builder.ts # 9 tools — funnel/page builder (internal API)
545
+ │ ├── funnel-builder.ts # 10 tools — funnel/page builder (internal API)
546
546
  │ ├── form-builder.ts # 5 tools — form builder (internal API)
547
547
  │ ├── pipeline-builder.ts # 5 tools — pipeline/stage builder (internal API)
548
548
  │ ├── bulk-operations.ts # 5 tools — batch contact operations
package/dist/index.js CHANGED
@@ -31,8 +31,8 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "@elitedcs/ghl-mcp",
34
- version: "3.6.0",
35
- description: "GoHighLevel MCP Server for Claude. 177 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
34
+ version: "3.7.0",
35
+ description: "GoHighLevel MCP Server for Claude. 178 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
36
36
  main: "dist/index.js",
37
37
  bin: {
38
38
  "ghl-mcp": "dist/index.js"
@@ -869,6 +869,14 @@ var WorkflowBuilderClient = class _WorkflowBuilderClient {
869
869
  getApiKey() {
870
870
  return this.apiKey;
871
871
  }
872
+ /**
873
+ * Return the current Firebase user ID. Required by the funnel-builder's
874
+ * delete_funnel endpoint which needs `userId` in the request body. Not
875
+ * exposed via MCP.
876
+ */
877
+ getUserId() {
878
+ return this.userId;
879
+ }
872
880
  /**
873
881
  * Probe Firebase auth by refreshing the ID token. Used by the health_check
874
882
  * diagnostic tool. Does not touch GHL backend — just exchanges the refresh
@@ -4397,6 +4405,8 @@ function registerFunnelBuilderTools(server2, builderClient) {
4397
4405
  }
4398
4406
  async function funnelRequest(method, path6, body) {
4399
4407
  const headers = await client.buildHeaders();
4408
+ headers.Origin = "https://app.gohighlevel.com";
4409
+ headers.Referer = "https://app.gohighlevel.com/";
4400
4410
  const url = `https://backend.leadconnectorhq.com/funnels${path6}`;
4401
4411
  const options = { method, headers };
4402
4412
  if (body && (method === "POST" || method === "PUT")) {
@@ -4502,21 +4512,92 @@ ${text2}`);
4502
4512
  );
4503
4513
  server2.tool(
4504
4514
  "update_funnel",
4505
- "Update a funnel's name, domain, or steps configuration.",
4515
+ "Update a funnel's settings: name, path/slug, tracking codes, GDPR + payment + chat widget flags, etc. This is the 'save settings panel' endpoint \u2014 it sends the entire settings object, so the tool fetches current settings first, merges your overrides on top, then POSTs back. To rename a single step (page) inside a funnel, use update_funnel_step instead.",
4506
4516
  {
4507
4517
  funnelId: import_zod32.z.string().describe("The funnel ID to update."),
4508
- name: import_zod32.z.string().optional().describe("New funnel name."),
4509
- steps: import_zod32.z.array(import_zod32.z.record(import_zod32.z.unknown())).optional().describe("Updated steps array with page references.")
4518
+ name: import_zod32.z.string().optional().describe("New funnel display name."),
4519
+ path: import_zod32.z.string().optional().describe("URL slug for the funnel (e.g., '/my-funnel'). Mapped to funnelPath."),
4520
+ bodyTrackingCode: import_zod32.z.string().optional().describe("Custom tracking script injected before </body>."),
4521
+ headTrackingCode: import_zod32.z.string().optional().describe("Custom tracking script injected before </head>."),
4522
+ chatWidgetId: import_zod32.z.string().optional().describe("Chat widget to embed on every page in this funnel."),
4523
+ domainId: import_zod32.z.string().optional().describe("Custom domain ID for the funnel."),
4524
+ faviconUrl: import_zod32.z.string().optional().describe("Favicon URL for funnel pages."),
4525
+ paymentMode: import_zod32.z.boolean().optional().describe("Whether to require a payment mode for the funnel."),
4526
+ requireCreditCard: import_zod32.z.boolean().optional().describe("Whether to require a credit card on opt-in steps."),
4527
+ isGdprCompliant: import_zod32.z.boolean().optional().describe("Toggle GDPR compliance mode."),
4528
+ isOptimisePageLoad: import_zod32.z.boolean().optional().describe("Enable GHL's page-load optimization."),
4529
+ imageOptimization: import_zod32.z.boolean().optional().describe("Enable automatic image optimization."),
4530
+ allowPaymentModeOption: import_zod32.z.boolean().optional().describe("Allow buyers to choose payment mode on checkout."),
4531
+ storeCurrencyFormatting: import_zod32.z.boolean().optional().describe("Use store-wide currency formatting.")
4510
4532
  },
4511
- async ({ funnelId, name, steps }) => {
4533
+ async (args) => {
4512
4534
  try {
4513
- const body = { locationId: client.locationId };
4514
- if (name !== void 0) body.name = name;
4515
- if (steps !== void 0) body.steps = steps;
4516
- const result = await funnelRequest("PUT", `/funnel/${funnelId}`, body);
4517
- return {
4518
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
4535
+ const current = await funnelRequest("GET", `/funnel/list?locationId=${client.locationId}&limit=200`);
4536
+ const funnels = Array.isArray(current.funnels) ? current.funnels : [];
4537
+ const found = funnels.find((f) => f._id === args.funnelId || f.id === args.funnelId);
4538
+ if (!found) {
4539
+ throw new Error(`Funnel ${args.funnelId} not found in this location.`);
4540
+ }
4541
+ const overrides = {};
4542
+ if (args.name !== void 0) overrides.funnelName = args.name;
4543
+ if (args.path !== void 0) overrides.funnelPath = args.path;
4544
+ if (args.bodyTrackingCode !== void 0) overrides.bodyTrackingCode = args.bodyTrackingCode;
4545
+ if (args.headTrackingCode !== void 0) overrides.headTrackingCode = args.headTrackingCode;
4546
+ if (args.chatWidgetId !== void 0) overrides.chatWidgetId = args.chatWidgetId;
4547
+ if (args.domainId !== void 0) overrides.domainId = args.domainId;
4548
+ if (args.faviconUrl !== void 0) overrides.faviconUrl = args.faviconUrl;
4549
+ if (args.paymentMode !== void 0) overrides.paymentMode = args.paymentMode;
4550
+ if (args.requireCreditCard !== void 0) overrides.requireCreditCard = args.requireCreditCard;
4551
+ if (args.isGdprCompliant !== void 0) overrides.isGdprCompliant = args.isGdprCompliant;
4552
+ if (args.isOptimisePageLoad !== void 0) overrides.isOptimisePageLoad = args.isOptimisePageLoad;
4553
+ if (args.imageOptimization !== void 0) overrides.imageOptimization = args.imageOptimization;
4554
+ if (args.allowPaymentModeOption !== void 0) overrides.allowPaymentModeOption = args.allowPaymentModeOption;
4555
+ if (args.storeCurrencyFormatting !== void 0) overrides.storeCurrencyFormatting = args.storeCurrencyFormatting;
4556
+ const body = {
4557
+ locationId: client.locationId,
4558
+ funnelId: args.funnelId,
4559
+ funnelName: found.name ?? "",
4560
+ funnelPath: found.url ?? "/",
4561
+ allowPaymentModeOption: found.allowPaymentModeOption ?? true,
4562
+ bodyTrackingCode: found.bodyTrackingCode ?? "",
4563
+ chatWidgetId: found.chatWidgetId ?? "",
4564
+ domainId: found.domainId ?? "",
4565
+ faviconUrl: found.faviconUrl ?? "",
4566
+ headTrackingCode: found.headTrackingCode ?? "",
4567
+ imageOptimization: found.imageOptimization ?? true,
4568
+ isGdprCompliant: found.isGdprCompliant ?? false,
4569
+ isOptimisePageLoad: found.isOptimisePageLoad ?? true,
4570
+ paymentMode: found.paymentMode ?? true,
4571
+ requireCreditCard: found.requireCreditCard ?? true,
4572
+ stopAllSplitTestsAndReset: null,
4573
+ storeCurrencyFormatting: found.storeCurrencyFormatting ?? false,
4574
+ ...overrides
4519
4575
  };
4576
+ const result = await funnelRequest("POST", `/funnel/update-settings`, body);
4577
+ return jsonResponse(result);
4578
+ } catch (error) {
4579
+ return errorResponse(error);
4580
+ }
4581
+ }
4582
+ );
4583
+ server2.tool(
4584
+ "update_funnel_step",
4585
+ "Update a single step (page entry) inside a funnel \u2014 display name, URL slug, or attached domain. A step is GHL's container for one or more pages (multiple pages exist when split-testing). Use update_funnel for funnel-level settings instead.",
4586
+ {
4587
+ funnelId: import_zod32.z.string().describe("The funnel ID containing the step."),
4588
+ stepId: import_zod32.z.string().describe("The step ID to update (from get_funnel_pages / list_funnels_full)."),
4589
+ name: import_zod32.z.string().optional().describe("New step display name."),
4590
+ url: import_zod32.z.string().optional().describe("URL slug for the step (e.g., '/thank-you')."),
4591
+ domainName: import_zod32.z.string().optional().describe("Custom domain to attach to this step.")
4592
+ },
4593
+ async ({ funnelId, stepId, name, url, domainName }) => {
4594
+ try {
4595
+ const body = { stepId };
4596
+ if (name !== void 0) body.name = name;
4597
+ if (url !== void 0) body.url = url;
4598
+ if (domainName !== void 0) body.domainName = domainName;
4599
+ const result = await funnelRequest("PUT", `/funnel/step/${funnelId}`, body);
4600
+ return jsonResponse(result);
4520
4601
  } catch (error) {
4521
4602
  return errorResponse(error);
4522
4603
  }
@@ -4524,17 +4605,19 @@ ${text2}`);
4524
4605
  );
4525
4606
  server2.tool(
4526
4607
  "delete_funnel",
4527
- "Permanently delete a funnel and all its pages. IRREVERSIBLE.",
4608
+ "Permanently delete a funnel and all its steps/pages. IRREVERSIBLE.",
4528
4609
  {
4529
4610
  funnelId: import_zod32.z.string().describe("The funnel ID to delete."),
4530
4611
  confirm: import_zod32.z.literal("DELETE").describe("Must pass 'DELETE' to confirm this destructive action.")
4531
4612
  },
4532
4613
  async ({ funnelId }) => {
4533
4614
  try {
4534
- const result = await funnelRequest("DELETE", `/funnel/${funnelId}?locationId=${client.locationId}`);
4535
- return {
4536
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
4537
- };
4615
+ const result = await funnelRequest("POST", `/funnel/delete`, {
4616
+ funnelId,
4617
+ locationId: client.locationId,
4618
+ userId: client.getUserId()
4619
+ });
4620
+ return jsonResponse(result);
4538
4621
  } catch (error) {
4539
4622
  return errorResponse(error);
4540
4623
  }
@@ -4542,23 +4625,30 @@ ${text2}`);
4542
4625
  );
4543
4626
  server2.tool(
4544
4627
  "create_funnel_page",
4545
- "Create a new page in a funnel. Returns the page ID which you can then update with content.",
4628
+ "Create a new step (page entry) in a funnel. The client generates a step UUID locally and sends it in the request; GHL auto-creates a blank page inside the step on the server side. The response includes the new step's id and the page's Firestore reference, which you can then update via update_page_content.",
4546
4629
  {
4547
- funnelId: import_zod32.z.string().describe("The funnel ID to add the page to."),
4548
- name: import_zod32.z.string().describe("Page name."),
4549
- url: import_zod32.z.string().optional().describe("URL slug for the page (e.g. '/thank-you').")
4630
+ funnelId: import_zod32.z.string().describe("The funnel ID to add the step to."),
4631
+ name: import_zod32.z.string().describe("Step display name."),
4632
+ url: import_zod32.z.string().optional().describe("URL slug for the step. Defaults to '' (blank) like the GHL UI."),
4633
+ type: import_zod32.z.string().optional().describe("Step type. Defaults to 'optin_funnel_page'.")
4550
4634
  },
4551
- async ({ funnelId, name, url }) => {
4635
+ async ({ funnelId, name, url, type }) => {
4552
4636
  try {
4553
- const result = await funnelRequest("POST", `/page?locationId=${client.locationId}`, {
4637
+ const stepId = crypto.randomUUID();
4638
+ const result = await funnelRequest("POST", `/funnel/create-step`, {
4554
4639
  funnelId,
4555
- name,
4556
- url: url || `/${name.toLowerCase().replace(/\s+/g, "-")}`,
4557
- locationId: client.locationId
4640
+ step: {
4641
+ id: stepId,
4642
+ name,
4643
+ url: url ?? "",
4644
+ pages: [],
4645
+ control_traffic: 100,
4646
+ split: false,
4647
+ type: type ?? "optin_funnel_page"
4648
+ }
4558
4649
  });
4559
- return {
4560
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
4561
- };
4650
+ const merged = typeof result === "object" && result !== null ? { stepId, ...result } : { stepId, result };
4651
+ return jsonResponse(merged);
4562
4652
  } catch (error) {
4563
4653
  return errorResponse(error);
4564
4654
  }
@@ -4566,20 +4656,22 @@ ${text2}`);
4566
4656
  );
4567
4657
  server2.tool(
4568
4658
  "update_page_content",
4569
- "Update a page's builder content \u2014 sections, elements, styles, tracking codes, popups. Use get_page_content first to see the current structure, modify it, and pass the updated JSON here.",
4659
+ "Update a page's builder content \u2014 sections, elements, settings, pageStyles, trackingCode, popups, popupsList, fontsForPreview. Use get_page_content first to see the current structure, modify it, then pass the full 8-field envelope here. GHL's UI fires three POSTs to this endpoint per save (one collaborative-edit broadcast + two commits); for MCP use, one POST with write=true is enough.",
4570
4660
  {
4571
4661
  pageId: import_zod32.z.string().describe("The page ID to update."),
4572
- content: import_zod32.z.record(import_zod32.z.unknown()).describe("The full page content JSON (sections, settings, general, pageStyles, trackingCode, popups, popupsList).")
4662
+ content: import_zod32.z.record(import_zod32.z.unknown()).describe("The full page content JSON. Expected fields: sections, settings, general, pageStyles, trackingCode, popups, popupsList, fontsForPreview."),
4663
+ isPublished: import_zod32.z.boolean().optional().describe("Whether the page should be marked published after the save. Defaults to false (draft).")
4573
4664
  },
4574
- async ({ pageId, content }) => {
4665
+ async ({ pageId, content, isPublished }) => {
4575
4666
  try {
4576
- const result = await funnelRequest("PUT", `/page/${pageId}?locationId=${client.locationId}`, {
4577
- ...content,
4578
- locationId: client.locationId
4667
+ const result = await funnelRequest("POST", `/builder/prebuilt-section/sync/changes`, {
4668
+ locationId: client.locationId,
4669
+ pageId,
4670
+ pageData: content,
4671
+ write: true,
4672
+ isPublished: isPublished ?? false
4579
4673
  });
4580
- return {
4581
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
4582
- };
4674
+ return jsonResponse(result);
4583
4675
  } catch (error) {
4584
4676
  return errorResponse(error);
4585
4677
  }
@@ -4587,17 +4679,16 @@ ${text2}`);
4587
4679
  );
4588
4680
  server2.tool(
4589
4681
  "delete_funnel_page",
4590
- "Permanently delete a page from a funnel. IRREVERSIBLE.",
4682
+ "Permanently delete a step (and all its pages) from a funnel. IRREVERSIBLE. SIGNATURE CHANGED in v3.7.0: was pageId, now requires funnelId + stepId to match GHL's actual delete-step endpoint.",
4591
4683
  {
4592
- pageId: import_zod32.z.string().describe("The page ID to delete."),
4684
+ funnelId: import_zod32.z.string().describe("The funnel ID containing the step."),
4685
+ stepId: import_zod32.z.string().describe("The step ID to delete (from get_funnel_pages / list_funnels_full)."),
4593
4686
  confirm: import_zod32.z.literal("DELETE").describe("Must pass 'DELETE' to confirm this destructive action.")
4594
4687
  },
4595
- async ({ pageId }) => {
4688
+ async ({ funnelId, stepId }) => {
4596
4689
  try {
4597
- const result = await funnelRequest("DELETE", `/page/${pageId}?locationId=${client.locationId}`);
4598
- return {
4599
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
4600
- };
4690
+ const result = await funnelRequest("POST", `/funnel/delete-step`, { funnelId, stepId });
4691
+ return jsonResponse(result);
4601
4692
  } catch (error) {
4602
4693
  return errorResponse(error);
4603
4694
  }
@@ -6633,7 +6724,7 @@ async function validateFirebase(firebaseKey, refreshToken) {
6633
6724
  function registerSetupTool(server2) {
6634
6725
  server2.tool(
6635
6726
  "setup_ghl_mcp",
6636
- "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 176 tools (168 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
6727
+ "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 178 tools (168 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
6637
6728
  {
6638
6729
  email: import_zod42.z.string().email().describe("Email used at purchase."),
6639
6730
  license_key: import_zod42.z.string().min(20).describe("License key from your purchase email."),
@@ -6688,7 +6779,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
6688
6779
  ghl_firebase_api_key: workflowBuilderEnabled ? args.ghl_firebase_api_key?.trim() : void 0,
6689
6780
  ghl_firebase_refresh_token: workflowBuilderEnabled ? args.ghl_firebase_refresh_token?.trim() : void 0
6690
6781
  });
6691
- const toolCount = workflowBuilderEnabled ? "176" : "168";
6782
+ const toolCount = workflowBuilderEnabled ? "178" : "168";
6692
6783
  const wfLine = workflowBuilderEnabled ? "Workflow Builder: enabled." : "Workflow Builder: not configured (optional).";
6693
6784
  const wfTip = workflowBuilderEnabled ? "" : "\nTo enable Workflow Builder later (8 extra tools): run enable_workflow_builder with your three Firebase values. No need to re-enter license/API key/location ID.";
6694
6785
  return {
@@ -6716,7 +6807,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
6716
6807
  function registerEnableWorkflowBuilderTool(server2) {
6717
6808
  server2.tool(
6718
6809
  "enable_workflow_builder",
6719
- "Add Firebase credentials to an existing GHL Command install to unlock the 8 workflow builder tools (create/edit/clone/delete/publish workflows, validate_workflow, get_trigger_registry). Requires you've already run setup_ghl_mcp. Capture the three Firebase values from your GHL browser session \u2014 see elitedcs.com/ghl-mcp-firebase for step-by-step DevTools instructions. Tool count goes from 168 to 176 after the next Claude restart.",
6810
+ "Add Firebase credentials to an existing GHL Command install to unlock the 9 workflow builder tools (create/edit/clone/delete/publish workflows, validate_workflow, get_trigger_registry). Requires you've already run setup_ghl_mcp. Capture the three Firebase values from your GHL browser session \u2014 see elitedcs.com/ghl-mcp-firebase for step-by-step DevTools instructions. Tool count goes from 168 to 178 after the next Claude restart.",
6720
6811
  {
6721
6812
  ghl_user_id: import_zod42.z.string().min(10).describe("Firebase User ID (uid). DevTools \u2192 Application \u2192 IndexedDB \u2192 firebaseLocalStorageDb \u2192 firebaseLocalStorage \u2192 the value.uid field of the firebase:authUser row."),
6722
6813
  ghl_firebase_api_key: import_zod42.z.string().min(10).describe("Firebase API Key starting with 'AIza'. The string between 'firebase:authUser:' and ':[DEFAULT]' in the row's Key column."),
@@ -6763,7 +6854,7 @@ DevTools steps: https://elitedcs.com/ghl-mcp-firebase`
6763
6854
  "",
6764
6855
  "Firebase credentials verified and saved.",
6765
6856
  "",
6766
- "**Restart Claude (quit fully and reopen) to load the 8 additional workflow builder tools (176 total).**",
6857
+ "**Restart Claude (quit fully and reopen) to load the workflow builder + funnel builder + pipeline builder + form builder + workflow cloner tools (178 total).**",
6767
6858
  "",
6768
6859
  'After restart, try: "List my workflows in full detail" or "Validate workflow <id>".',
6769
6860
  "",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.6.0",
4
- "description": "GoHighLevel MCP Server for Claude. 177 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
3
+ "version": "3.7.0",
4
+ "description": "GoHighLevel MCP Server for Claude. 178 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "ghl-mcp": "dist/index.js"