@elitedcs/ghl-mcp 3.17.1 → 3.19.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,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.19.0 — npm lead capture + $97 pricing alignment
4
+
5
+ **One new bootstrap-mode tool. Normal-mode count unchanged (212 across 43 modules).**
6
+
7
+ - **`request_license`** (bootstrap mode): someone who installs from npm without a license can leave their email instead of hitting a dead end. It records them as a tracked GHL lead (`ghl-command-lead` tag → lead-nurture sequence, New Lead opportunity) and returns the purchase link. Turns anonymous npm installs into sellable pipeline.
8
+ - Bootstrap messaging now points no-license users to `request_license`.
9
+ - Pricing aligned to **$97 one-time** across the README (was inconsistently listed as $297); corrected the post-setup count to 163 core tools (212 with the optional Workflow Builder Firebase add-on).
10
+
11
+ ## 3.18.0 — Social Planner: location-scoped endpoints, scheduled posts, typed media
12
+
13
+ **No new tools (still 212 across 43 modules) — fixes plus a capability add to the existing Social Planner tools.**
14
+
15
+ - Every Social Planner call (`get_social_posts`, `get_social_post`, `delete_social_post`, `get_social_media_accounts`, `create_social_post`) now hits the correct location-scoped v2 path (`/social-media-posting/{locationId}/...`). The bare paths were returning errors.
16
+ - `create_social_post` now supports **scheduled posts**: pass `scheduledAt` (sent as `scheduleDate`) with `status: "scheduled"`. `status` is a typed enum: `in_review`, `scheduled`, `draft`, `published`.
17
+ - Media items are typed — each URL is sent as `{ url, type }` with the MIME type inferred. Scheduled posts reject untyped media (and Instagram requires at least one media item).
18
+
3
19
  ## 3.17.1 — Onboarding messaging fixes + public feedback tracker
4
20
 
5
21
  **No tool changes — still 212 across 43 modules.** Bug-driven fixes from a buyer support ticket (Ryan Thomas, 2026-05-25), plus a public place to file feedback.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Works with **both the Claude Desktop App and Claude Code terminal** — your choice.
8
8
 
9
- **License required.** Buy at [elitedcs.com/ghl-mcp-server](https://elitedcs.com/ghl-mcp-server) — one-time $297, three-machine activation, no subscription.
9
+ **License required.** Buy at [elitedcs.com/ghl-mcp-server](https://elitedcs.com/ghl-mcp-server) — one-time $97, three-machine activation, no subscription.
10
10
 
11
11
  Built by [Elite DCs, LLC](https://elitedcs.com).
12
12
 
@@ -61,7 +61,7 @@ This MCP (Model Context Protocol) server connects Claude directly to GoHighLevel
61
61
 
62
62
  ### 1. Buy a license
63
63
 
64
- [elitedcs.com/ghl-mcp-server](https://elitedcs.com/ghl-mcp-server) — one-time $297. License key arrives by email within 1-2 minutes.
64
+ [elitedcs.com/ghl-mcp-server](https://elitedcs.com/ghl-mcp-server) — one-time $97. License key arrives by email within 1-2 minutes.
65
65
 
66
66
  ### 2. Install in Claude Desktop App
67
67
 
@@ -98,7 +98,7 @@ Run setup_ghl_mcp to activate GHL Command:
98
98
  ghl_location_id: YOUR_LOCATION_ID
99
99
  ```
100
100
 
101
- Approve the tool call. Server validates your license, verifies your GHL credentials, writes them to a per-user config file. **Quit Claude one more time and reopen** — all 169 tools are now unlocked.
101
+ Approve the tool call. Server validates your license, verifies your GHL credentials, writes them to a per-user config file. **Quit Claude one more time and reopen** — all 163 core tools are now unlocked (212 with the optional Workflow Builder Firebase add-on).
102
102
 
103
103
  ### 4. Try it
104
104
 
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "@elitedcs/ghl-mcp",
34
- version: "3.17.1",
34
+ version: "3.19.0",
35
35
  description: "GoHighLevel MCP Server for Claude. 212 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
36
36
  main: "dist/index.js",
37
37
  bin: {
@@ -3606,18 +3606,22 @@ function registerSocialPlannerTools(server2, client) {
3606
3606
  fromDate: import_zod21.z.string().optional().describe("Start date filter (ISO string)"),
3607
3607
  toDate: import_zod21.z.string().optional().describe("End date filter (ISO string)"),
3608
3608
  includeUsers: import_zod21.z.boolean().optional().describe("Whether to include user details"),
3609
- status: import_zod21.z.enum(["in_review", "scheduled", "published", "failed", "in_progress"]).optional().describe("Post status filter")
3609
+ status: import_zod21.z.enum(["in_review", "scheduled", "published", "failed", "in_progress"]).optional().describe("Post status filter"),
3610
+ skip: import_zod21.z.number().optional().describe("Pagination offset"),
3611
+ limit: import_zod21.z.number().optional().describe("Page size")
3610
3612
  },
3611
- async ({ locationId: locationId2, type, accounts, fromDate, toDate, includeUsers, status }) => {
3613
+ async ({ locationId: locationId2, type, accounts, fromDate, toDate, includeUsers, status, skip, limit }) => {
3612
3614
  const resolvedLocationId = client.resolveLocationId(locationId2);
3613
- const params = { locationId: resolvedLocationId };
3614
- if (type !== void 0) params.type = type;
3615
- if (accounts !== void 0) params.accounts = accounts;
3616
- if (fromDate !== void 0) params.fromDate = fromDate;
3617
- if (toDate !== void 0) params.toDate = toDate;
3618
- if (includeUsers !== void 0) params.includeUsers = includeUsers;
3619
- if (status !== void 0) params.status = status;
3620
- return client.get("/social-media-posting/", { params });
3615
+ const body = {};
3616
+ if (type !== void 0) body.type = type;
3617
+ if (accounts !== void 0) body.accounts = accounts;
3618
+ if (fromDate !== void 0) body.fromDate = fromDate;
3619
+ if (toDate !== void 0) body.toDate = toDate;
3620
+ if (includeUsers !== void 0) body.includeUsers = includeUsers;
3621
+ if (status !== void 0) body.status = status;
3622
+ body.skip = skip ?? 0;
3623
+ body.limit = limit ?? 20;
3624
+ return client.post(`/social-media-posting/${resolvedLocationId}/posts/list`, { body });
3621
3625
  }
3622
3626
  );
3623
3627
  safeTool(
@@ -3625,10 +3629,12 @@ function registerSocialPlannerTools(server2, client) {
3625
3629
  "get_social_post",
3626
3630
  "Get a single social media post by ID",
3627
3631
  {
3628
- postId: import_zod21.z.string().describe("The social media post ID")
3632
+ postId: import_zod21.z.string().describe("The social media post ID"),
3633
+ locationId: import_zod21.z.string().optional().describe("GHL Location ID (optional if GHL_LOCATION_ID is set)")
3629
3634
  },
3630
- async ({ postId }) => {
3631
- return client.get(`/social-media-posting/${postId}`);
3635
+ async ({ postId, locationId: locationId2 }) => {
3636
+ const resolvedLocationId = client.resolveLocationId(locationId2);
3637
+ return client.get(`/social-media-posting/${resolvedLocationId}/posts/${postId}`);
3632
3638
  }
3633
3639
  );
3634
3640
  safeTool(
@@ -3636,13 +3642,16 @@ function registerSocialPlannerTools(server2, client) {
3636
3642
  "delete_social_post",
3637
3643
  "Delete a social media post",
3638
3644
  {
3639
- postId: import_zod21.z.string().describe("The social media post ID to delete")
3645
+ postId: import_zod21.z.string().describe("The social media post ID to delete"),
3646
+ locationId: import_zod21.z.string().optional().describe("GHL Location ID (optional if GHL_LOCATION_ID is set)")
3640
3647
  },
3641
- async ({ postId }) => {
3642
- return client.delete(`/social-media-posting/${postId}`);
3648
+ async ({ postId, locationId: locationId2 }) => {
3649
+ const resolvedLocationId = client.resolveLocationId(locationId2);
3650
+ return client.delete(`/social-media-posting/${resolvedLocationId}/posts/${postId}`);
3643
3651
  }
3644
3652
  );
3645
- server2.tool(
3653
+ safeTool(
3654
+ server2,
3646
3655
  "get_social_media_accounts",
3647
3656
  "Get connected social media accounts for a location",
3648
3657
  {
@@ -3650,21 +3659,7 @@ function registerSocialPlannerTools(server2, client) {
3650
3659
  },
3651
3660
  async ({ locationId: locationId2 }) => {
3652
3661
  const resolvedLocationId = client.resolveLocationId(locationId2);
3653
- try {
3654
- const result = await client.get(
3655
- `/social-media-posting/oauth/${resolvedLocationId}/accounts`
3656
- );
3657
- return jsonResponse(result);
3658
- } catch {
3659
- try {
3660
- const result = await client.get(
3661
- `/social-media-posting/oauth/facebook/accounts/${resolvedLocationId}`
3662
- );
3663
- return jsonResponse(result);
3664
- } catch (fallbackError) {
3665
- return errorResponse(fallbackError);
3666
- }
3667
- }
3662
+ return client.get(`/social-media-posting/${resolvedLocationId}/accounts`);
3668
3663
  }
3669
3664
  );
3670
3665
  safeTool(
@@ -3674,9 +3669,11 @@ function registerSocialPlannerTools(server2, client) {
3674
3669
  {
3675
3670
  locationId: import_zod21.z.string().optional().describe("GHL Location ID (optional if GHL_LOCATION_ID is set)"),
3676
3671
  accountIds: import_zod21.z.array(import_zod21.z.string()).describe("Array of social account IDs to post to"),
3672
+ userId: import_zod21.z.string().optional().describe("GHL user ID that authors the post (required by the API; falls back to GHL_USER_ID env)"),
3677
3673
  summary: import_zod21.z.string().optional().describe("The post text/caption"),
3678
3674
  media: import_zod21.z.array(import_zod21.z.string()).optional().describe("Array of media URLs to attach"),
3679
- status: import_zod21.z.enum(["in_review", "scheduled", "draft"]).optional().describe("Post status"),
3675
+ type: import_zod21.z.enum(["post", "story", "reel"]).optional().describe("Post type (default post)"),
3676
+ status: import_zod21.z.enum(["in_review", "scheduled", "draft", "published"]).optional().describe("Post status"),
3680
3677
  scheduledAt: import_zod21.z.string().optional().describe("Scheduled publish time (ISO string)"),
3681
3678
  tags: import_zod21.z.array(import_zod21.z.string()).optional().describe("Tags for the post"),
3682
3679
  ogData: import_zod21.z.object({
@@ -3685,16 +3682,41 @@ function registerSocialPlannerTools(server2, client) {
3685
3682
  image: import_zod21.z.string().optional()
3686
3683
  }).optional().describe("Open Graph data for link previews")
3687
3684
  },
3688
- async ({ locationId: locationId2, accountIds, summary, media, status, scheduledAt, tags, ogData }) => {
3685
+ async ({ locationId: locationId2, accountIds, userId, summary, media, type, status, scheduledAt, tags, ogData }) => {
3689
3686
  const resolvedLocationId = client.resolveLocationId(locationId2);
3690
- const body = { locationId: resolvedLocationId, accountIds };
3687
+ const resolvedUserId = userId ?? process.env.GHL_USER_ID;
3688
+ if (!resolvedUserId) {
3689
+ throw new Error(
3690
+ "create_social_post requires a userId (the post author). Pass userId or set GHL_USER_ID."
3691
+ );
3692
+ }
3693
+ const body = {
3694
+ accountIds,
3695
+ userId: resolvedUserId,
3696
+ type: type ?? "post"
3697
+ };
3691
3698
  if (summary !== void 0) body.summary = summary;
3692
- if (media !== void 0) body.media = media;
3699
+ if (media !== void 0) {
3700
+ const mimeFor = (url) => {
3701
+ const ext = url.split("?")[0].split(".").pop()?.toLowerCase() ?? "";
3702
+ const map = {
3703
+ png: "image/png",
3704
+ jpg: "image/jpeg",
3705
+ jpeg: "image/jpeg",
3706
+ gif: "image/gif",
3707
+ webp: "image/webp",
3708
+ mp4: "video/mp4",
3709
+ mov: "video/quicktime"
3710
+ };
3711
+ return map[ext] ?? "image/png";
3712
+ };
3713
+ body.media = media.map((url) => ({ url, type: mimeFor(url) }));
3714
+ }
3693
3715
  if (status !== void 0) body.status = status;
3694
- if (scheduledAt !== void 0) body.scheduledAt = scheduledAt;
3716
+ if (scheduledAt !== void 0) body.scheduleDate = scheduledAt;
3695
3717
  if (tags !== void 0) body.tags = tags;
3696
3718
  if (ogData !== void 0) body.ogData = ogData;
3697
- return client.post("/social-media-posting/", { body });
3719
+ return client.post(`/social-media-posting/${resolvedLocationId}/posts`, { body });
3698
3720
  }
3699
3721
  );
3700
3722
  }
@@ -5747,6 +5769,7 @@ var os2 = __toESM(require("os"));
5747
5769
  var crypto2 = __toESM(require("crypto"));
5748
5770
  var import_zod37 = require("zod");
5749
5771
  var LICENSE_API = "https://elitedcs.com/api/validate-license";
5772
+ var CAPTURE_API = "https://elitedcs.com/api/capture-lead";
5750
5773
  var GHL_API = "https://services.leadconnectorhq.com";
5751
5774
  var FIREBASE_TOKEN_API = "https://securetoken.googleapis.com/v1/token";
5752
5775
  function deviceFingerprint() {
@@ -5955,6 +5978,48 @@ DevTools steps: https://elitedcs.com/ghl-mcp-firebase`
5955
5978
  }
5956
5979
  );
5957
5980
  }
5981
+ function registerLeadCaptureTool(server2) {
5982
+ server2.tool(
5983
+ "request_license",
5984
+ "Get a GHL Command license. Use this if you installed from npm but don't have a license yet (or setup_ghl_mcp says your license is missing/invalid). GHL Command is $97 one-time \u2014 212 tools across 43 modules, 3-machine activation, no subscription. Leave your email and we'll send the purchase link + setup help; the tool also returns where to buy right now.",
5985
+ {
5986
+ email: import_zod37.z.string().email().describe("Your email \u2014 where to send the purchase link and setup help."),
5987
+ name: import_zod37.z.string().optional().describe("Your name (optional).")
5988
+ },
5989
+ async (args) => {
5990
+ const buyUrl = "https://elitedcs.com/ghl-mcp-server";
5991
+ try {
5992
+ const res = await fetch(CAPTURE_API, {
5993
+ method: "POST",
5994
+ headers: { "Content-Type": "application/json" },
5995
+ body: JSON.stringify({ email: args.email.trim(), name: args.name?.trim(), source: "npm-bootstrap" })
5996
+ });
5997
+ const data = await res.json().catch(() => ({}));
5998
+ const url = data.purchaseUrl || buyUrl;
5999
+ return {
6000
+ content: [{
6001
+ type: "text",
6002
+ text: [
6003
+ data.message || "You're on the list.",
6004
+ "",
6005
+ "GHL Command is $97 one-time: 212 tools across 43 modules, including the only programmatic GHL workflow builder. 3 machines, no subscription.",
6006
+ "",
6007
+ `Buy now: ${url}`,
6008
+ "Your license key arrives by email within a couple minutes. Then run setup_ghl_mcp with your email + key."
6009
+ ].join("\n")
6010
+ }]
6011
+ };
6012
+ } catch {
6013
+ return {
6014
+ content: [{
6015
+ type: "text",
6016
+ text: `Buy your GHL Command license ($97 one-time) at ${buyUrl} \u2014 your key arrives by email within a couple minutes, then run setup_ghl_mcp with your email + key.`
6017
+ }]
6018
+ };
6019
+ }
6020
+ }
6021
+ );
6022
+ }
5958
6023
 
5959
6024
  // src/tools/location-switcher.ts
5960
6025
  function routeFirebaseForCompany(builderClient, registry2, companyId) {
@@ -8261,10 +8326,11 @@ if (inBootstrapMode) {
8261
8326
  process.stderr.write(
8262
8327
  `[ghl-mcp] Bootstrap mode: no credentials found.
8263
8328
  [ghl-mcp] Looked for env vars (GHL_API_KEY, GHL_LOCATION_ID) and credentials file (${credentialsPath()}).
8264
- [ghl-mcp] Only setup_ghl_mcp and get_mcp_version are available. Run setup_ghl_mcp from Claude with your license + GHL credentials.
8329
+ [ghl-mcp] Only setup_ghl_mcp, request_license, and get_mcp_version are available. Run setup_ghl_mcp with your license + GHL credentials \u2014 or request_license if you don't have a license yet.
8265
8330
  `
8266
8331
  );
8267
8332
  registerSetupTool(server);
8333
+ registerLeadCaptureTool(server);
8268
8334
  } else {
8269
8335
  const client = new GHLClient({ apiKey, locationId });
8270
8336
  registerAllTools(server, client, registry, pkg.version);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.17.1",
3
+ "version": "3.19.0",
4
4
  "description": "GoHighLevel MCP Server for Claude. 212 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {