@elitedcs/ghl-mcp 3.11.1 → 3.13.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,103 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.13.0 — Memberships read + 200-tool milestone (gap-closure round 6, final)
4
+
5
+ **201 tools across 43 modules. Bundle: ~318 KB.**
6
+
7
+ Closes the last Phase-2 gap. Memberships read tools — and the tool count crosses 200.
8
+
9
+ ### 3 new tools (Firebase-gated, read-only)
10
+
11
+ - **`list_membership_offers`** — offers + products in one call (`{products, offers}`). Products are courses/communities; offers are the access grants.
12
+ - **`list_membership_categories`** — all course categories in a location.
13
+ - **`list_membership_lessons`** — all course lessons in a location.
14
+
15
+ Most useful for fetching the IDs that membership trigger conditions reference: `offer_access_granted`, `product_completed`, `category_completed`, `lesson_completed`, etc.
16
+
17
+ ### Endpoint discovery
18
+
19
+ From the workflow bundle's `LocationMembership` class:
20
+ ```js
21
+ membershipUrl = `${config.membershipURL}` // → backend.leadconnectorhq.com/membership
22
+ endpoint = `${membershipUrl}/smart-list/offers-products/${locationId}`
23
+ ```
24
+
25
+ Verified endpoints (Firebase auth):
26
+ - `GET /membership/smart-list/offers-products/{loc}`
27
+ - `GET /membership/smart-list/location/{loc}/workflow?type=category`
28
+ - `GET /membership/smart-list/location/{loc}/workflow?type=lesson`
29
+
30
+ Bearer/PIT returns 401 — Firebase-gated, same as the other internal-API tools.
31
+
32
+ ### Read-only by design
33
+
34
+ The workflow bundle only READS membership data (to populate trigger/action dropdowns). Creating/editing courses, offers, and lessons lives in the Memberships app and isn't exposed here — that would need a DevTools capture against a live account with actual courses. The per-product (`?product_id=`) and per-category (`/category/{id}/lessons`) filtered endpoints returned 401 for synthetic IDs and couldn't be verified without real course data, so they're not exposed; the location-scoped "list all" endpoints return everything anyway.
35
+
36
+ ### Phase-2 complete
37
+
38
+ All 6 Phase-2 gap areas from the v3.8.1 analysis have now been addressed:
39
+
40
+ | Area | Result |
41
+ |---|---|
42
+ | Email templates | list/create/update (v3.10.0) |
43
+ | SMS/snippet templates | list-only — write is scope-walled (v3.10.1) |
44
+ | Smart Lists | full CRUD (v3.11.0) |
45
+ | Reputation | review-link-list; reviews capture-pending (v3.11.1) |
46
+ | Email campaigns | list (fixed) + create-draft; send capture-pending (v3.12.0) |
47
+ | Memberships | read-only (this release) |
48
+
49
+ The recurring limiter: high-value write actions (send a campaign, list reviews, create a course, delete a template) sit behind either GHL's IAM wall or location-resolution quirks that need a DevTools capture against a live account. Read + create-draft surfaces were reachable; full write automation for those areas is the next frontier when a live-account capture session happens.
50
+
51
+ ### Tool count impact
52
+
53
+ - Total: 198 → 201 (+3) — crosses 200
54
+ - Modules: 42 → 43
55
+
56
+ ### Files changed
57
+
58
+ - `src/tools/memberships.ts` — NEW
59
+ - `src/tools/index.ts` — registered membership tools
60
+ - `src/setup-tool.ts` — count 198 → 201
61
+
62
+ ## 3.12.0 — Email campaigns: create-draft + list fix (gap-closure round 5)
63
+
64
+ **198 tools across 42 modules. Bundle: ~316 KB.**
65
+
66
+ ### Bug fix: `get_email_campaigns` was broken
67
+
68
+ The existing tool hit `/emails/` which returns 404 — it had been returning errors for buyers. Repointed to `/emails/schedule` (the real email-broadcast list endpoint). Now returns campaigns with id, name, status (draft/scheduled/sent), templateId, and send config. Added an optional `limit` param. (For the older automation-campaign-builder feature, `get_campaigns` is the separate tool.)
69
+
70
+ ### New tool: `create_email_campaign` (Firebase-gated)
71
+
72
+ Create an email campaign/broadcast DRAFT from an existing email template. Required: `templateId` (make one with `create_email_template`). Optional: name, subject, fromName, fromEmail, isPlainText, enableResendToUnopened, hasUtmTracking.
73
+
74
+ ### The IAM wall + the workaround
75
+
76
+ `POST /emails/schedule` with a bearer (Private Integration) token returns `"This route is not yet supported by the IAM Service"` — a hard GHL wall. **Firebase auth gets past it** (same internal-API path as workflow-builder). So create_email_campaign is Firebase-gated.
77
+
78
+ ### Honest scope: draft-only
79
+
80
+ The campaign is created as `status: "draft"`. What's NOT available via any discoverable endpoint (probed bearer + Firebase, every path variant):
81
+ - **send / schedule-to-send** — finish in the GHL UI (Marketing → Emails)
82
+ - **delete** — drafts are removed via the GHL UI
83
+ - **update** a draft
84
+ - **get single** campaign
85
+
86
+ These live in the Marketing app and would need a DevTools capture against a live account. The create tool's response includes a `_note` field stating the draft-only limitation so the LLM relays it to the buyer. Despite the limits, this gets a campaign ~90% built programmatically (template, subject, sender, tracking all set) — the buyer just clicks send.
87
+
88
+ ### Tool count impact
89
+
90
+ - Total: 197 → 198 (+1 new; get_email_campaigns is a fix, not a new tool)
91
+ - Modules: 41 → 42 (new email-campaigns module)
92
+ - create_email_campaign is Firebase-gated; get_email_campaigns works on public API
93
+
94
+ ### Files changed
95
+
96
+ - `src/tools/emails.ts` — fixed get_email_campaigns endpoint (/emails/ → /emails/schedule)
97
+ - `src/tools/email-campaigns.ts` — NEW: create_email_campaign (Firebase)
98
+ - `src/tools/index.ts` — registered email-campaigns module
99
+ - `src/setup-tool.ts` — count 197 → 198
100
+
3
101
  ## 3.11.1 — Reputation: review-link list (partial gap-closure round 4)
4
102
 
5
103
  **197 tools across 41 modules. Bundle: ~315 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.** 197 tools across 41 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.** 201 tools across 43 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
 
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.11.1",
35
- description: "GoHighLevel MCP Server for Claude. 197 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
34
+ version: "3.13.0",
35
+ description: "GoHighLevel MCP Server for Claude. 201 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"
@@ -3583,15 +3583,16 @@ function registerEmailTools(server2, client) {
3583
3583
  safeTool(
3584
3584
  server2,
3585
3585
  "get_email_campaigns",
3586
- "List email campaigns for a location.",
3586
+ "List email campaigns / broadcasts (scheduled email sends) for a location. Returns each campaign's id, name, status (draft / scheduled / sent), templateId, send config, and stats. FIXED in v3.12.0: previously hit /emails/ (404); now correctly uses /emails/schedule. For automation campaigns (the older campaign-builder feature), use get_campaigns instead.",
3587
3587
  {
3588
- locationId: import_zod24.z.string().optional().describe("GHL Location ID (optional if GHL_LOCATION_ID is set)")
3588
+ locationId: import_zod24.z.string().optional().describe("GHL Location ID (optional if GHL_LOCATION_ID is set)."),
3589
+ limit: import_zod24.z.number().optional().describe("Max campaigns to return.")
3589
3590
  },
3590
- async ({ locationId: locationId2 }) => {
3591
+ async ({ locationId: locationId2, limit }) => {
3591
3592
  const resolvedLocationId = client.resolveLocationId(locationId2);
3592
- return client.get("/emails/", {
3593
- params: { locationId: resolvedLocationId }
3594
- });
3593
+ const params = { locationId: resolvedLocationId };
3594
+ if (limit !== void 0) params.limit = limit;
3595
+ return client.get("/emails/schedule", { params });
3595
3596
  }
3596
3597
  );
3597
3598
  safeTool(
@@ -6189,42 +6190,167 @@ ${text2}`);
6189
6190
  );
6190
6191
  }
6191
6192
 
6192
- // src/tools/template-deployer.ts
6193
+ // src/tools/email-campaigns.ts
6193
6194
  var import_zod42 = require("zod");
6195
+ var SVC_BASE = "https://services.leadconnectorhq.com";
6196
+ function registerEmailCampaignTools(server2, builderClient) {
6197
+ const client = builderClient;
6198
+ if (!client) return;
6199
+ server2.tool(
6200
+ "create_email_campaign",
6201
+ "Create an email campaign / broadcast DRAFT from an existing email template. Requires a templateId (create one first with create_email_template). The campaign is created as a draft \u2014 to actually SEND or schedule it, finish in the GHL UI: the send/schedule endpoint isn't available through the API yet. There's also no API delete for campaigns, so drafts created here are removed via the GHL UI. Despite those limits, this gets the campaign 90% built \u2014 template, subject, sender, name all set programmatically.",
6202
+ {
6203
+ templateId: import_zod42.z.string().describe("ID of an email template (from create_email_template or list_email_templates) to use as the campaign body."),
6204
+ name: import_zod42.z.string().optional().describe("Internal campaign name (shown in the campaigns list, not to recipients). Defaults to a GHL-generated name."),
6205
+ subject: import_zod42.z.string().optional().describe("Email subject line recipients see."),
6206
+ fromName: import_zod42.z.string().optional().describe("Sender display name."),
6207
+ fromEmail: import_zod42.z.string().optional().describe("Sender email address. Must be a verified sending address in the location."),
6208
+ isPlainText: import_zod42.z.boolean().optional().describe("Send as plain text instead of HTML. Defaults to false."),
6209
+ enableResendToUnopened: import_zod42.z.boolean().optional().describe("Auto-resend to contacts who didn't open. Defaults to false."),
6210
+ hasUtmTracking: import_zod42.z.boolean().optional().describe("Append UTM tracking params to links. Defaults to false."),
6211
+ locationId: import_zod42.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
6212
+ },
6213
+ async ({ templateId, name, subject, fromName, fromEmail, isPlainText, enableResendToUnopened, hasUtmTracking, locationId: locationId2 }) => {
6214
+ try {
6215
+ const loc = locationId2 ?? client.locationId;
6216
+ const body = { locationId: loc, templateId };
6217
+ if (name !== void 0) body.name = name;
6218
+ if (subject !== void 0) body.subject = subject;
6219
+ if (fromName !== void 0) body.fromName = fromName;
6220
+ if (fromEmail !== void 0) body.fromEmail = fromEmail;
6221
+ if (isPlainText !== void 0) body.isPlainText = isPlainText;
6222
+ if (enableResendToUnopened !== void 0) body.enableResendToUnopened = enableResendToUnopened;
6223
+ if (hasUtmTracking !== void 0) body.hasUtmTracking = hasUtmTracking;
6224
+ const headers = await client.buildHeaders();
6225
+ const response = await fetch(`${SVC_BASE}/emails/schedule`, {
6226
+ method: "POST",
6227
+ headers,
6228
+ body: JSON.stringify(body)
6229
+ });
6230
+ if (!response.ok) {
6231
+ const text2 = await response.text();
6232
+ throw new Error(`Email Campaign API Error ${response.status}: POST /emails/schedule
6233
+ ${text2}`);
6234
+ }
6235
+ const text = await response.text();
6236
+ const result = text ? JSON.parse(text) : {};
6237
+ return jsonResponse({
6238
+ ...result,
6239
+ _note: "Campaign created as a DRAFT. To send or schedule it, open it in the GHL UI (Marketing \u2192 Emails) \u2014 the send endpoint isn't available via API. To delete a draft, use the GHL UI."
6240
+ });
6241
+ } catch (error) {
6242
+ return errorResponse(error);
6243
+ }
6244
+ }
6245
+ );
6246
+ }
6247
+
6248
+ // src/tools/memberships.ts
6249
+ var import_zod43 = require("zod");
6250
+ var MEMBERSHIP_BASE = "https://backend.leadconnectorhq.com/membership";
6251
+ function registerMembershipTools(server2, builderClient) {
6252
+ const client = builderClient;
6253
+ if (!client) return;
6254
+ async function membershipRequest(path6) {
6255
+ const headers = await client.buildHeaders();
6256
+ const response = await fetch(`${MEMBERSHIP_BASE}${path6}`, { method: "GET", headers });
6257
+ if (!response.ok) {
6258
+ const text2 = await response.text();
6259
+ throw new Error(`Membership API Error ${response.status}: GET ${path6}
6260
+ ${text2}`);
6261
+ }
6262
+ const text = await response.text();
6263
+ if (!text) return {};
6264
+ return JSON.parse(text);
6265
+ }
6266
+ server2.tool(
6267
+ "list_membership_offers",
6268
+ "List a location's membership offers and products in one call. Returns { products: [...], offers: [...] }. Products are courses/communities; offers are the access grants (what a contact gets enrolled in). Use the returned ids with membership trigger conditions like offer_access_granted / product_completed. READ-ONLY \u2014 creating offers/products happens in the GHL Memberships UI.",
6269
+ {
6270
+ locationId: import_zod43.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
6271
+ },
6272
+ async ({ locationId: locationId2 }) => {
6273
+ try {
6274
+ const loc = locationId2 ?? client.locationId;
6275
+ const result = await membershipRequest(`/smart-list/offers-products/${loc}`);
6276
+ return jsonResponse(result);
6277
+ } catch (error) {
6278
+ return errorResponse(error);
6279
+ }
6280
+ }
6281
+ );
6282
+ server2.tool(
6283
+ "list_membership_categories",
6284
+ "List all membership/course categories in a location. Categories group lessons inside a course/product. Use the returned ids with the category_completed / category_started trigger conditions. READ-ONLY.",
6285
+ {
6286
+ limit: import_zod43.z.number().optional().describe("Max categories to return. Defaults to a large value (effectively all)."),
6287
+ locationId: import_zod43.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
6288
+ },
6289
+ async ({ limit, locationId: locationId2 }) => {
6290
+ try {
6291
+ const loc = locationId2 ?? client.locationId;
6292
+ const result = await membershipRequest(`/smart-list/location/${loc}/workflow?type=category&limit=${limit ?? 1e5}`);
6293
+ return jsonResponse(result);
6294
+ } catch (error) {
6295
+ return errorResponse(error);
6296
+ }
6297
+ }
6298
+ );
6299
+ server2.tool(
6300
+ "list_membership_lessons",
6301
+ "List all membership/course lessons in a location. Use the returned ids with the lesson_completed / lesson_started trigger conditions. READ-ONLY.",
6302
+ {
6303
+ limit: import_zod43.z.number().optional().describe("Max lessons to return. Defaults to a large value (effectively all)."),
6304
+ locationId: import_zod43.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
6305
+ },
6306
+ async ({ limit, locationId: locationId2 }) => {
6307
+ try {
6308
+ const loc = locationId2 ?? client.locationId;
6309
+ const result = await membershipRequest(`/smart-list/location/${loc}/workflow?type=lesson&limit=${limit ?? 1e5}`);
6310
+ return jsonResponse(result);
6311
+ } catch (error) {
6312
+ return errorResponse(error);
6313
+ }
6314
+ }
6315
+ );
6316
+ }
6317
+
6318
+ // src/tools/template-deployer.ts
6319
+ var import_zod44 = require("zod");
6194
6320
  var fs3 = __toESM(require("fs"));
6195
6321
  var path3 = __toESM(require("path"));
6196
6322
  function delay3(ms) {
6197
6323
  return new Promise((resolve5) => setTimeout(resolve5, ms));
6198
6324
  }
6199
- var TemplateSchema = import_zod42.z.object({
6200
- templateName: import_zod42.z.string(),
6201
- templateVersion: import_zod42.z.string().optional(),
6202
- description: import_zod42.z.string().optional().default(""),
6203
- questionnaire: import_zod42.z.array(import_zod42.z.object({
6204
- id: import_zod42.z.string(),
6205
- question: import_zod42.z.string(),
6206
- type: import_zod42.z.string(),
6207
- required: import_zod42.z.boolean().optional(),
6208
- placeholder: import_zod42.z.string().optional()
6325
+ var TemplateSchema = import_zod44.z.object({
6326
+ templateName: import_zod44.z.string(),
6327
+ templateVersion: import_zod44.z.string().optional(),
6328
+ description: import_zod44.z.string().optional().default(""),
6329
+ questionnaire: import_zod44.z.array(import_zod44.z.object({
6330
+ id: import_zod44.z.string(),
6331
+ question: import_zod44.z.string(),
6332
+ type: import_zod44.z.string(),
6333
+ required: import_zod44.z.boolean().optional(),
6334
+ placeholder: import_zod44.z.string().optional()
6209
6335
  })).optional().default([]),
6210
- location: import_zod42.z.record(import_zod42.z.unknown()).optional(),
6211
- tags: import_zod42.z.array(import_zod42.z.string()).optional(),
6212
- customFields: import_zod42.z.array(import_zod42.z.object({
6213
- name: import_zod42.z.string(),
6214
- dataType: import_zod42.z.string()
6336
+ location: import_zod44.z.record(import_zod44.z.unknown()).optional(),
6337
+ tags: import_zod44.z.array(import_zod44.z.string()).optional(),
6338
+ customFields: import_zod44.z.array(import_zod44.z.object({
6339
+ name: import_zod44.z.string(),
6340
+ dataType: import_zod44.z.string()
6215
6341
  })).optional(),
6216
- pipelines: import_zod42.z.array(import_zod42.z.object({
6217
- name: import_zod42.z.string(),
6218
- stages: import_zod42.z.array(import_zod42.z.object({ position: import_zod42.z.number(), name: import_zod42.z.string() }))
6342
+ pipelines: import_zod44.z.array(import_zod44.z.object({
6343
+ name: import_zod44.z.string(),
6344
+ stages: import_zod44.z.array(import_zod44.z.object({ position: import_zod44.z.number(), name: import_zod44.z.string() }))
6219
6345
  })).optional(),
6220
- workflows: import_zod42.z.array(import_zod42.z.object({
6221
- name: import_zod42.z.string(),
6222
- condition: import_zod42.z.string().optional(),
6223
- actions: import_zod42.z.array(import_zod42.z.record(import_zod42.z.unknown())).optional().default([])
6346
+ workflows: import_zod44.z.array(import_zod44.z.object({
6347
+ name: import_zod44.z.string(),
6348
+ condition: import_zod44.z.string().optional(),
6349
+ actions: import_zod44.z.array(import_zod44.z.record(import_zod44.z.unknown())).optional().default([])
6224
6350
  })).optional(),
6225
- calendars: import_zod42.z.array(import_zod42.z.object({
6226
- name: import_zod42.z.string(),
6227
- description: import_zod42.z.string().optional()
6351
+ calendars: import_zod44.z.array(import_zod44.z.object({
6352
+ name: import_zod44.z.string(),
6353
+ description: import_zod44.z.string().optional()
6228
6354
  })).optional()
6229
6355
  });
6230
6356
  function registerTemplateDeployerTools(server2, client) {
@@ -6295,7 +6421,7 @@ function registerTemplateDeployerTools(server2, client) {
6295
6421
  "get_template_questionnaire",
6296
6422
  "Get the questionnaire for a specific template. Returns all the questions that need to be answered before deploying. Present these to the user one at a time in a conversational style.",
6297
6423
  {
6298
- templateFile: import_zod42.z.string().describe("Path to the template JSON file (from list_templates).")
6424
+ templateFile: import_zod44.z.string().describe("Path to the template JSON file (from list_templates).")
6299
6425
  },
6300
6426
  async ({ templateFile }) => {
6301
6427
  try {
@@ -6328,10 +6454,10 @@ function registerTemplateDeployerTools(server2, client) {
6328
6454
  "deploy_template",
6329
6455
  "Deploy a template to set up a GHL sub-account. Creates tags, custom fields, pipelines with stages, calendars, workflows, and forms based on the template and the user's questionnaire answers. This is the main setup automation tool.",
6330
6456
  {
6331
- templateFile: import_zod42.z.string().describe("Path to the template JSON file."),
6332
- answers: import_zod42.z.record(import_zod42.z.unknown()).describe("Questionnaire answers keyed by question ID (e.g. {business_name: 'My Clinic', business_phone: '+15551234567', ...})."),
6333
- locationId: import_zod42.z.string().optional().describe("Location ID to deploy to. Uses default if not specified."),
6334
- dryRun: import_zod42.z.boolean().optional().describe("If true, shows what would be created without actually creating anything. Defaults to false.")
6457
+ templateFile: import_zod44.z.string().describe("Path to the template JSON file."),
6458
+ answers: import_zod44.z.record(import_zod44.z.unknown()).describe("Questionnaire answers keyed by question ID (e.g. {business_name: 'My Clinic', business_phone: '+15551234567', ...})."),
6459
+ locationId: import_zod44.z.string().optional().describe("Location ID to deploy to. Uses default if not specified."),
6460
+ dryRun: import_zod44.z.boolean().optional().describe("If true, shows what would be created without actually creating anything. Defaults to false.")
6335
6461
  },
6336
6462
  async ({ templateFile, answers, locationId: locationId2, dryRun }) => {
6337
6463
  try {
@@ -6577,7 +6703,7 @@ ${errors.join("\n")}` : "\nNo errors!",
6577
6703
  }
6578
6704
 
6579
6705
  // src/tools/validators.ts
6580
- var import_zod43 = require("zod");
6706
+ var import_zod45 = require("zod");
6581
6707
  function extractFromTrigger(trigger, refs) {
6582
6708
  const triggerName = String(trigger.name ?? trigger.type ?? "unnamed trigger");
6583
6709
  const where = `trigger "${triggerName}"`;
@@ -6712,7 +6838,7 @@ function registerValidatorTools(server2, client, builderClient) {
6712
6838
  "validate_workflow",
6713
6839
  "Pre-flight ID validation for a deployed GHL workflow. Scans every trigger and action for references to pipelines, pipeline stages, custom fields, users, workflows, forms, calendars, and surveys; verifies each ID exists in the current location. Use this BEFORE publish_workflow when a workflow has been edited, or whenever a published workflow stops behaving as expected. Catches the silent-failure bug where invalid IDs make GHL skip all subsequent actions without warning. Returns a structured report of valid + missing references.",
6714
6840
  {
6715
- workflowId: import_zod43.z.string().describe("The workflow ID to validate.")
6841
+ workflowId: import_zod45.z.string().describe("The workflow ID to validate.")
6716
6842
  },
6717
6843
  async ({ workflowId }) => {
6718
6844
  try {
@@ -7082,6 +7208,8 @@ function registerAllTools(server2, client, registry2, mcpVersion) {
7082
7208
  registerWorkflowClonerTools(server2, builderClient);
7083
7209
  registerSmartListTools(server2, builderClient);
7084
7210
  registerReputationTools(server2, builderClient);
7211
+ registerEmailCampaignTools(server2, builderClient);
7212
+ registerMembershipTools(server2, builderClient);
7085
7213
  registerValidatorTools(server2, client, builderClient);
7086
7214
  registerDiagnosticTools(server2, mcpVersion ?? "unknown", client, builderClient, registry2 ?? null);
7087
7215
  registerLocationSwitcherTools(server2, client, builderClient, registry2, mcpVersion);
@@ -7091,18 +7219,18 @@ function registerAllTools(server2, client, registry2, mcpVersion) {
7091
7219
  var fs4 = __toESM(require("fs"));
7092
7220
  var path4 = __toESM(require("path"));
7093
7221
  var os = __toESM(require("os"));
7094
- var import_zod44 = require("zod");
7222
+ var import_zod46 = require("zod");
7095
7223
  var APP_NAME = "elitedcs-ghl-mcp";
7096
- var CredentialsSchema = import_zod44.z.object({
7097
- license_key: import_zod44.z.string().min(1),
7098
- email: import_zod44.z.string().email(),
7099
- verified_at: import_zod44.z.string().min(1),
7100
- ghl_api_key: import_zod44.z.string().min(1),
7101
- ghl_location_id: import_zod44.z.string().min(1),
7102
- ghl_company_id: import_zod44.z.string().optional(),
7103
- ghl_user_id: import_zod44.z.string().optional(),
7104
- ghl_firebase_api_key: import_zod44.z.string().optional(),
7105
- ghl_firebase_refresh_token: import_zod44.z.string().optional()
7224
+ var CredentialsSchema = import_zod46.z.object({
7225
+ license_key: import_zod46.z.string().min(1),
7226
+ email: import_zod46.z.string().email(),
7227
+ verified_at: import_zod46.z.string().min(1),
7228
+ ghl_api_key: import_zod46.z.string().min(1),
7229
+ ghl_location_id: import_zod46.z.string().min(1),
7230
+ ghl_company_id: import_zod46.z.string().optional(),
7231
+ ghl_user_id: import_zod46.z.string().optional(),
7232
+ ghl_firebase_api_key: import_zod46.z.string().optional(),
7233
+ ghl_firebase_refresh_token: import_zod46.z.string().optional()
7106
7234
  });
7107
7235
  function appDataDir() {
7108
7236
  const home = os.homedir();
@@ -7152,7 +7280,7 @@ function writeCredentials(creds) {
7152
7280
  // src/setup-tool.ts
7153
7281
  var os2 = __toESM(require("os"));
7154
7282
  var crypto3 = __toESM(require("crypto"));
7155
- var import_zod45 = require("zod");
7283
+ var import_zod47 = require("zod");
7156
7284
  var LICENSE_API = "https://elitedcs.com/api/validate-license";
7157
7285
  var GHL_API = "https://services.leadconnectorhq.com";
7158
7286
  var FIREBASE_TOKEN_API = "https://securetoken.googleapis.com/v1/token";
@@ -7221,16 +7349,16 @@ async function validateFirebase(firebaseKey, refreshToken) {
7221
7349
  function registerSetupTool(server2) {
7222
7350
  server2.tool(
7223
7351
  "setup_ghl_mcp",
7224
- "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 197 tools (161 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
7352
+ "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 201 tools (161 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
7225
7353
  {
7226
- email: import_zod45.z.string().email().describe("Email used at purchase."),
7227
- license_key: import_zod45.z.string().min(20).describe("License key from your purchase email."),
7228
- ghl_api_key: import_zod45.z.string().min(10).describe("GHL Private Integration key (starts with 'pit-'). Created INSIDE the sub-account at Settings > Integrations > Private Integrations."),
7229
- ghl_location_id: import_zod45.z.string().min(10).describe("GHL Location ID (sub-account ID). Found in your GHL URL: /location/THIS_PART/dashboard."),
7230
- ghl_company_id: import_zod45.z.string().optional().describe("(Agency only) Company ID for multi-location access."),
7231
- ghl_user_id: import_zod45.z.string().optional().describe("(Workflow Builder, optional) Firebase User ID. See README for browser capture instructions."),
7232
- ghl_firebase_api_key: import_zod45.z.string().optional().describe("(Workflow Builder, optional) Firebase API Key starting with 'AIza'."),
7233
- ghl_firebase_refresh_token: import_zod45.z.string().optional().describe("(Workflow Builder, optional) Firebase refresh token starting with 'AMf-'.")
7354
+ email: import_zod47.z.string().email().describe("Email used at purchase."),
7355
+ license_key: import_zod47.z.string().min(20).describe("License key from your purchase email."),
7356
+ ghl_api_key: import_zod47.z.string().min(10).describe("GHL Private Integration key (starts with 'pit-'). Created INSIDE the sub-account at Settings > Integrations > Private Integrations."),
7357
+ ghl_location_id: import_zod47.z.string().min(10).describe("GHL Location ID (sub-account ID). Found in your GHL URL: /location/THIS_PART/dashboard."),
7358
+ ghl_company_id: import_zod47.z.string().optional().describe("(Agency only) Company ID for multi-location access."),
7359
+ ghl_user_id: import_zod47.z.string().optional().describe("(Workflow Builder, optional) Firebase User ID. See README for browser capture instructions."),
7360
+ ghl_firebase_api_key: import_zod47.z.string().optional().describe("(Workflow Builder, optional) Firebase API Key starting with 'AIza'."),
7361
+ ghl_firebase_refresh_token: import_zod47.z.string().optional().describe("(Workflow Builder, optional) Firebase refresh token starting with 'AMf-'.")
7234
7362
  },
7235
7363
  async (args) => {
7236
7364
  const lic = await validateLicense(args.email, args.license_key);
@@ -7276,7 +7404,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
7276
7404
  ghl_firebase_api_key: workflowBuilderEnabled ? args.ghl_firebase_api_key?.trim() : void 0,
7277
7405
  ghl_firebase_refresh_token: workflowBuilderEnabled ? args.ghl_firebase_refresh_token?.trim() : void 0
7278
7406
  });
7279
- const toolCount = workflowBuilderEnabled ? "197" : "161";
7407
+ const toolCount = workflowBuilderEnabled ? "201" : "161";
7280
7408
  const wfLine = workflowBuilderEnabled ? "Workflow Builder: enabled." : "Workflow Builder: not configured (optional).";
7281
7409
  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.";
7282
7410
  return {
@@ -7304,11 +7432,11 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
7304
7432
  function registerEnableWorkflowBuilderTool(server2) {
7305
7433
  server2.tool(
7306
7434
  "enable_workflow_builder",
7307
- "Add Firebase credentials to an existing GHL Command install to unlock 30 additional tools across 6 modules: workflow builder (create/edit/clone/delete/publish/validate workflows, build_if_else_branch, build_goal_event, get_trigger_registry), funnel + page builder (10 tools), form builder (5 tools), pipeline builder (5 tools), and workflow cloning. 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 161 to 197 after the next Claude restart.",
7435
+ "Add Firebase credentials to an existing GHL Command install to unlock 30 additional tools across 6 modules: workflow builder (create/edit/clone/delete/publish/validate workflows, build_if_else_branch, build_goal_event, get_trigger_registry), funnel + page builder (10 tools), form builder (5 tools), pipeline builder (5 tools), and workflow cloning. 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 161 to 201 after the next Claude restart.",
7308
7436
  {
7309
- ghl_user_id: import_zod45.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."),
7310
- ghl_firebase_api_key: import_zod45.z.string().min(10).describe("Firebase API Key starting with 'AIza'. The string between 'firebase:authUser:' and ':[DEFAULT]' in the row's Key column."),
7311
- ghl_firebase_refresh_token: import_zod45.z.string().min(10).describe("Firebase refresh token. value.stsTokenManager.refreshToken in the firebase:authUser row.")
7437
+ ghl_user_id: import_zod47.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."),
7438
+ ghl_firebase_api_key: import_zod47.z.string().min(10).describe("Firebase API Key starting with 'AIza'. The string between 'firebase:authUser:' and ':[DEFAULT]' in the row's Key column."),
7439
+ ghl_firebase_refresh_token: import_zod47.z.string().min(10).describe("Firebase refresh token. value.stsTokenManager.refreshToken in the firebase:authUser row.")
7312
7440
  },
7313
7441
  async (args) => {
7314
7442
  const existing = readCredentials();
@@ -7351,7 +7479,7 @@ DevTools steps: https://elitedcs.com/ghl-mcp-firebase`
7351
7479
  "",
7352
7480
  "Firebase credentials verified and saved.",
7353
7481
  "",
7354
- "**Restart Claude (quit fully and reopen) to load the workflow builder + funnel builder + pipeline builder + form builder + workflow cloner tools (197 total).**",
7482
+ "**Restart Claude (quit fully and reopen) to load the workflow builder + funnel builder + pipeline builder + form builder + workflow cloner tools (201 total).**",
7355
7483
  "",
7356
7484
  'After restart, try: "List my workflows in full detail" or "Validate workflow <id>".',
7357
7485
  "",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.11.1",
4
- "description": "GoHighLevel MCP Server for Claude. 197 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
3
+ "version": "3.13.0",
4
+ "description": "GoHighLevel MCP Server for Claude. 201 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"