@elitedcs/ghl-mcp 3.17.0 → 3.18.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,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.18.0 — Social Planner: location-scoped endpoints, scheduled posts, typed media
4
+
5
+ **No new tools (still 212 across 43 modules) — fixes plus a capability add to the existing Social Planner tools.**
6
+
7
+ - 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.
8
+ - `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`.
9
+ - 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).
10
+
11
+ ## 3.17.1 — Onboarding messaging fixes + public feedback tracker
12
+
13
+ **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.
14
+
15
+ ### Firebase / Workflow Builder onboarding was steering buyers wrong
16
+
17
+ `workflow_builder_status` told users to "set env vars in start-mcp.sh or .env" to enable the 49 Firebase-gated tools. On the npm / Claude Desktop install path there is no `start-mcp.sh` or `.env`, so buyers added the Firebase values to the `claude_desktop_config.json` env block — which Claude Desktop passes unreliably — and got `health_check` → "Firebase: SKIP" after a restart. Now both `workflow_builder_status` and the `health_check` Firebase-SKIP detail steer to `enable_workflow_builder` (the supported path: it validates the values and writes them to `credentials.json`), explicitly warn off the config env-var path, and demote the raw env vars to an advanced/self-hosted note.
18
+
19
+ ### Dead npm "Repository" link → public feedback repo
20
+
21
+ `package.json` `repository`/`bugs` pointed at the **private** source repo, which npm renders as a clickable link that 404s for buyers. Now both point to the new public feedback tracker — **https://github.com/drjerryrelth/ghl-command-feedback** — where users can file bug reports and feature requests via issue templates.
22
+
3
23
  ## 3.17.0 — Multi-tenant bootstrap, internal hardening, test coverage
4
24
 
5
25
  **No new tools — still 212 across 43 modules. Internal hardening + a multi-tenant fix.**
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.0",
34
+ version: "3.18.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: {
@@ -68,10 +68,11 @@ var require_package = __commonJS({
68
68
  homepage: "https://elitedcs.com/ghl-mcp-server",
69
69
  repository: {
70
70
  type: "git",
71
- url: "git+https://github.com/drjerryrelth/elite-dcs-ghl-mcp.git"
71
+ url: "git+https://github.com/drjerryrelth/ghl-command-feedback.git"
72
72
  },
73
73
  bugs: {
74
- url: "https://github.com/drjerryrelth/elite-dcs-ghl-mcp/issues"
74
+ url: "https://github.com/drjerryrelth/ghl-command-feedback/issues",
75
+ email: "support@cliniclaunchlab.com"
75
76
  },
76
77
  publishConfig: {
77
78
  access: "public"
@@ -3605,18 +3606,22 @@ function registerSocialPlannerTools(server2, client) {
3605
3606
  fromDate: import_zod21.z.string().optional().describe("Start date filter (ISO string)"),
3606
3607
  toDate: import_zod21.z.string().optional().describe("End date filter (ISO string)"),
3607
3608
  includeUsers: import_zod21.z.boolean().optional().describe("Whether to include user details"),
3608
- 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")
3609
3612
  },
3610
- async ({ locationId: locationId2, type, accounts, fromDate, toDate, includeUsers, status }) => {
3613
+ async ({ locationId: locationId2, type, accounts, fromDate, toDate, includeUsers, status, skip, limit }) => {
3611
3614
  const resolvedLocationId = client.resolveLocationId(locationId2);
3612
- const params = { locationId: resolvedLocationId };
3613
- if (type !== void 0) params.type = type;
3614
- if (accounts !== void 0) params.accounts = accounts;
3615
- if (fromDate !== void 0) params.fromDate = fromDate;
3616
- if (toDate !== void 0) params.toDate = toDate;
3617
- if (includeUsers !== void 0) params.includeUsers = includeUsers;
3618
- if (status !== void 0) params.status = status;
3619
- 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 });
3620
3625
  }
3621
3626
  );
3622
3627
  safeTool(
@@ -3624,10 +3629,12 @@ function registerSocialPlannerTools(server2, client) {
3624
3629
  "get_social_post",
3625
3630
  "Get a single social media post by ID",
3626
3631
  {
3627
- 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)")
3628
3634
  },
3629
- async ({ postId }) => {
3630
- 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}`);
3631
3638
  }
3632
3639
  );
3633
3640
  safeTool(
@@ -3635,13 +3642,16 @@ function registerSocialPlannerTools(server2, client) {
3635
3642
  "delete_social_post",
3636
3643
  "Delete a social media post",
3637
3644
  {
3638
- 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)")
3639
3647
  },
3640
- async ({ postId }) => {
3641
- 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}`);
3642
3651
  }
3643
3652
  );
3644
- server2.tool(
3653
+ safeTool(
3654
+ server2,
3645
3655
  "get_social_media_accounts",
3646
3656
  "Get connected social media accounts for a location",
3647
3657
  {
@@ -3649,21 +3659,7 @@ function registerSocialPlannerTools(server2, client) {
3649
3659
  },
3650
3660
  async ({ locationId: locationId2 }) => {
3651
3661
  const resolvedLocationId = client.resolveLocationId(locationId2);
3652
- try {
3653
- const result = await client.get(
3654
- `/social-media-posting/oauth/${resolvedLocationId}/accounts`
3655
- );
3656
- return jsonResponse(result);
3657
- } catch {
3658
- try {
3659
- const result = await client.get(
3660
- `/social-media-posting/oauth/facebook/accounts/${resolvedLocationId}`
3661
- );
3662
- return jsonResponse(result);
3663
- } catch (fallbackError) {
3664
- return errorResponse(fallbackError);
3665
- }
3666
- }
3662
+ return client.get(`/social-media-posting/${resolvedLocationId}/accounts`);
3667
3663
  }
3668
3664
  );
3669
3665
  safeTool(
@@ -3673,9 +3669,11 @@ function registerSocialPlannerTools(server2, client) {
3673
3669
  {
3674
3670
  locationId: import_zod21.z.string().optional().describe("GHL Location ID (optional if GHL_LOCATION_ID is set)"),
3675
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)"),
3676
3673
  summary: import_zod21.z.string().optional().describe("The post text/caption"),
3677
3674
  media: import_zod21.z.array(import_zod21.z.string()).optional().describe("Array of media URLs to attach"),
3678
- 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"),
3679
3677
  scheduledAt: import_zod21.z.string().optional().describe("Scheduled publish time (ISO string)"),
3680
3678
  tags: import_zod21.z.array(import_zod21.z.string()).optional().describe("Tags for the post"),
3681
3679
  ogData: import_zod21.z.object({
@@ -3684,16 +3682,41 @@ function registerSocialPlannerTools(server2, client) {
3684
3682
  image: import_zod21.z.string().optional()
3685
3683
  }).optional().describe("Open Graph data for link previews")
3686
3684
  },
3687
- async ({ locationId: locationId2, accountIds, summary, media, status, scheduledAt, tags, ogData }) => {
3685
+ async ({ locationId: locationId2, accountIds, userId, summary, media, type, status, scheduledAt, tags, ogData }) => {
3688
3686
  const resolvedLocationId = client.resolveLocationId(locationId2);
3689
- 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
+ };
3690
3698
  if (summary !== void 0) body.summary = summary;
3691
- 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
+ }
3692
3715
  if (status !== void 0) body.status = status;
3693
- if (scheduledAt !== void 0) body.scheduledAt = scheduledAt;
3716
+ if (scheduledAt !== void 0) body.scheduleDate = scheduledAt;
3694
3717
  if (tags !== void 0) body.tags = tags;
3695
3718
  if (ogData !== void 0) body.ogData = ogData;
3696
- return client.post("/social-media-posting/", { body });
3719
+ return client.post(`/social-media-posting/${resolvedLocationId}/posts`, { body });
3697
3720
  }
3698
3721
  );
3699
3722
  }
@@ -4791,13 +4814,21 @@ function registerWorkflowBuilderTools(server2, client) {
4791
4814
  if (!client) {
4792
4815
  server2.tool(
4793
4816
  "workflow_builder_status",
4794
- "Check if the workflow builder (internal API) is configured. Requires GHL_FIREBASE_API_KEY, GHL_FIREBASE_REFRESH_TOKEN, GHL_API_KEY, GHL_LOCATION_ID, and GHL_USER_ID.",
4817
+ "Check if the workflow builder (internal API) is configured. Needs Firebase credentials, added via enable_workflow_builder.",
4795
4818
  {},
4796
4819
  async () => ({
4797
4820
  content: [
4798
4821
  {
4799
4822
  type: "text",
4800
- text: "Workflow builder is NOT configured. Missing required env vars: GHL_FIREBASE_API_KEY, GHL_FIREBASE_REFRESH_TOKEN, GHL_USER_ID. Set these in start-mcp.sh or .env to enable full workflow editing."
4823
+ text: [
4824
+ "Workflow builder is NOT configured (Firebase credentials missing).",
4825
+ "",
4826
+ "To enable the 49 Firebase-gated tools, run enable_workflow_builder with your three Firebase values (ghl_user_id, ghl_firebase_api_key, ghl_firebase_refresh_token) captured from a logged-in GHL browser tab. Step-by-step DevTools guide: https://elitedcs.com/ghl-mcp-firebase",
4827
+ "",
4828
+ "Do NOT add these as env vars in your Claude Desktop config \u2014 that path is unreliable and is the usual reason this still shows missing after a restart. enable_workflow_builder validates the values and saves them for you; then fully quit and reopen Claude.",
4829
+ "",
4830
+ "(Advanced / self-hosted wrapper only: GHL_FIREBASE_API_KEY, GHL_FIREBASE_REFRESH_TOKEN, GHL_USER_ID via start-mcp.sh or .env.)"
4831
+ ].join("\n")
4801
4832
  }
4802
4833
  ],
4803
4834
  isError: true
@@ -8041,7 +8072,7 @@ function registerDiagnosticTools(server2, installedVersion, client, builderClien
8041
8072
  })();
8042
8073
  const firebasePromise = (async () => {
8043
8074
  if (!builderClient) {
8044
- return { name: "Firebase auth (workflow builder)", status: "skip", detail: "Not configured. The 49 Firebase-gated tools need Firebase credentials. The other 163 tools work fine without. To add it: run enable_workflow_builder with the three Firebase values from your GHL browser session (see elitedcs.com/ghl-mcp-firebase for DevTools steps)." };
8075
+ return { name: "Firebase auth (workflow builder)", status: "skip", detail: "Not configured. The 49 Firebase-gated tools need Firebase credentials. The other 163 tools work fine without. To add it: run enable_workflow_builder with the three Firebase values from your GHL browser session (see elitedcs.com/ghl-mcp-firebase for DevTools steps). Do NOT put Firebase values as env vars in your Claude Desktop config \u2014 that path is unreliable and is the usual reason this still shows skip after a restart. enable_workflow_builder saves and verifies them for you." };
8045
8076
  }
8046
8077
  const result = await builderClient.checkAuth();
8047
8078
  const activeCompany = builderClient.getCurrentCompanyId();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.17.0",
3
+ "version": "3.18.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": {
@@ -37,10 +37,11 @@
37
37
  "homepage": "https://elitedcs.com/ghl-mcp-server",
38
38
  "repository": {
39
39
  "type": "git",
40
- "url": "git+https://github.com/drjerryrelth/elite-dcs-ghl-mcp.git"
40
+ "url": "git+https://github.com/drjerryrelth/ghl-command-feedback.git"
41
41
  },
42
42
  "bugs": {
43
- "url": "https://github.com/drjerryrelth/elite-dcs-ghl-mcp/issues"
43
+ "url": "https://github.com/drjerryrelth/ghl-command-feedback/issues",
44
+ "email": "support@cliniclaunchlab.com"
44
45
  },
45
46
  "publishConfig": {
46
47
  "access": "public"