@elitedcs/ghl-mcp 3.27.1 → 3.28.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,59 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.28.0 — Tool allowlist (issue #1): cut context cost on big installs
4
+
5
+ Two new optional env vars let you gate which tools register at startup so
6
+ unused schemas never load into context. Closes [issue #1](https://github.com/drjerryrelth/ghl-command-feedback/issues/1).
7
+
8
+ **New env vars (both optional, both default-empty):**
9
+
10
+ - `GHL_ENABLED_MODULES` — comma-separated module names to register
11
+ (e.g. `contacts,conversations,locations,custom-objects`).
12
+ - `GHL_ENABLED_TOOLS` — comma-separated tool names to register
13
+ (e.g. `search_contacts,get_contact,update_custom_value`).
14
+
15
+ **Filter precedence:**
16
+
17
+ - Neither set → every tool registers (backward compatible).
18
+ - Modules only → tools in those modules register.
19
+ - Tools only → those exact tool names register.
20
+ - Both set → UNION (a tool registers if its module is enabled OR its
21
+ name is explicitly listed).
22
+
23
+ **Always-on tools** never get filtered out, so setup and recovery still
24
+ work even with a heavily-restricted allowlist:
25
+ `setup_ghl_mcp`, `request_license`, `get_mcp_version`,
26
+ `auto_capture_firebase_script`, `enable_workflow_builder`, `health_check`.
27
+
28
+ **Validation + logging:**
29
+
30
+ - Whitespace and commas both work as separators. Matching is case-insensitive.
31
+ - Unrecognized names log a one-line WARNING to stderr and are ignored
32
+ (startup never aborts).
33
+ - When the allowlist is active, startup logs one summary:
34
+ `Tool allowlist active: registered N of M tools (modules=[...]; explicit-tools=[...])`.
35
+ - When unset, zero extra noise — existing logs are unchanged.
36
+
37
+ **Motivation (from the issue):** running 25+ registered locations with
38
+ all 212 tools registered burned tokens on every message in chats that
39
+ didn't need GHL. Now buyers can restrict the surface to the modules
40
+ they actually use.
41
+
42
+ ## 3.27.2 — Fix license metadata: MIT → Proprietary
43
+
44
+ Metadata-only release. Corrects the npm package's stated license, which
45
+ was wrongly set to MIT and contradicted the paid/proprietary nature of
46
+ the product.
47
+
48
+ - `package.json` `license` field: `"MIT"` → `"SEE LICENSE IN LICENSE"`
49
+ - `LICENSE` file replaced with the actual proprietary commercial license
50
+ (Copyright Elite DCs, LLC; usage requires a paid license from
51
+ elitedcs.com/ghl-mcp-server; no redistribution, no reverse engineering,
52
+ no license-gate circumvention).
53
+
54
+ No code changes. Same 212-tool surface as 3.27.1. The runtime license-key
55
+ validation flow is unchanged.
56
+
3
57
  ## 3.27.1 — README: real buyer testimonials + 30-day guarantee
4
58
 
5
59
  Documentation-only release. Surfaces four real, verbatim buyer replies on the npm package README:
package/LICENSE CHANGED
@@ -1,21 +1,23 @@
1
- MIT License
1
+ GHL Command - Proprietary Software License
2
+ Copyright (c) 2026 Elite DCs, LLC. All rights reserved.
2
3
 
3
- Copyright (c) 2026 Elite DCs, LLC
4
+ 1. Overview
5
+ GHL Command (the "Software") is commercial, proprietary software owned by Elite DCs, LLC (the "Licensor"). The Software is licensed, not sold. Use of the Software requires a valid paid license obtained from https://elitedcs.com/ghl-mcp-server.
4
6
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
7
+ 2. License Grant
8
+ Subject to payment of the applicable fee and ongoing compliance with the Licensor's then-current commercial terms, Licensor grants the purchaser a non-exclusive, non-transferable, revocable license to install and use the Software on the number of machines specified at purchase. No other rights are granted.
11
9
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
10
+ 3. Restrictions
11
+ Except as expressly permitted by the commercial terms or by applicable law, you may not: (a) copy, modify, or create derivative works of the Software; (b) distribute, sublicense, sell, rent, lease, or otherwise transfer the Software; (c) reverse engineer, decompile, or disassemble the Software, or attempt to derive its source code; (d) remove or alter any proprietary notices; or (e) circumvent or disable any license-validation or activation mechanism.
14
12
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
13
+ 4. Ownership
14
+ The Software and all intellectual property rights therein are and remain the exclusive property of Elite DCs, LLC. This license does not transfer any ownership interest.
15
+
16
+ 5. No Warranty
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
18
+
19
+ 6. Limitation of Liability
20
+ TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL ELITE DCS, LLC BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ 7. Contact
23
+ Elite DCs, LLC - https://elitedcs.com/ghl-mcp-server
package/README.md CHANGED
@@ -709,9 +709,48 @@ Source repo is private. Contributors need an invitation from `drjerryrelth`. The
709
709
  | `GHL_USER_ID` | No* | Your GHL user ID. Required for workflow builder tools. |
710
710
  | `GHL_FIREBASE_API_KEY` | No* | Firebase API key from GHL's auth system. Required for workflow builder tools. |
711
711
  | `GHL_FIREBASE_REFRESH_TOKEN` | No* | Firebase refresh token. Required for workflow builder tools. May rotate periodically. |
712
+ | `GHL_ENABLED_MODULES` | No | Comma-separated module names to limit which tool groups register at startup (e.g. `contacts,conversations,locations`). Unset = every module registers (default). See "Reducing context / token usage" below. |
713
+ | `GHL_ENABLED_TOOLS` | No | Comma-separated individual tool names to register (e.g. `search_contacts,get_contact,update_custom_value`). Set alongside `GHL_ENABLED_MODULES` to add specific tools on top of whole-module enables. |
712
714
 
713
715
  *Required only for the workflow builder (internal API) tools. The standard API tools work without these.
714
716
 
717
+ ### Reducing context / token usage
718
+
719
+ Every registered MCP tool's schema is shipped to the model on every message. With 212 tools that's a meaningful per-message context cost even in chats that never touch GHL. If you only use a slice of GHL Command, restrict the tool surface with `GHL_ENABLED_MODULES` and/or `GHL_ENABLED_TOOLS`:
720
+
721
+ ```jsonc
722
+ // Claude Desktop config — enable whole modules
723
+ "env": {
724
+ "GHL_API_KEY": "pit-...",
725
+ "GHL_LOCATION_ID": "...",
726
+ "GHL_ENABLED_MODULES": "contacts,conversations,locations,custom-objects"
727
+ }
728
+ ```
729
+
730
+ ```jsonc
731
+ // Or pin to individual tools
732
+ "env": {
733
+ "GHL_API_KEY": "pit-...",
734
+ "GHL_LOCATION_ID": "...",
735
+ "GHL_ENABLED_TOOLS": "search_contacts,get_contact,update_custom_value"
736
+ }
737
+ ```
738
+
739
+ Rules of the road:
740
+
741
+ - **Neither set → every tool registers** (default, backward compatible — your existing config keeps working unchanged).
742
+ - **`GHL_ENABLED_MODULES` only** → register tools in those modules.
743
+ - **`GHL_ENABLED_TOOLS` only** → register only those exact tool names.
744
+ - **Both set** → register the **union**: a tool registers if its module is in `GHL_ENABLED_MODULES` OR its name is in `GHL_ENABLED_TOOLS`. (Whole modules plus a few extra tools.)
745
+ - Whitespace and commas both work as separators. Matching is case-insensitive.
746
+ - Always-on tools register regardless of the allowlist so setup and recovery still work: `setup_ghl_mcp`, `request_license`, `get_mcp_version`, `auto_capture_firebase_script`, `enable_workflow_builder`, `health_check`.
747
+ - Unrecognized module/tool names log a one-line WARNING to stderr and are ignored. Startup never aborts.
748
+ - On startup with the allowlist active, the server logs one summary line: `Tool allowlist active: registered N of M tools (...)`.
749
+
750
+ **Valid module names** (use these in `GHL_ENABLED_MODULES`):
751
+
752
+ `contacts`, `conversations`, `opportunities`, `calendars`, `locations`, `workflows`, `funnels`, `forms`, `surveys`, `payments`, `products`, `invoices`, `campaigns`, `users`, `media`, `social-planner`, `courses`, `businesses`, `blogs`, `emails`, `trigger-links`, `custom-objects`, `associations`, `estimates`, `coupons`, `webhooks`, `documents`, `bulk-operations`, `account-export`, `template-deployer`, `workflow-builder`, `funnel-builder`, `form-builder`, `pipeline-builder`, `workflow-cloner`, `smart-lists`, `reputation`, `email-campaigns`, `email-builder`, `memberships`, `validators`, `diagnostics`, `location-switcher`
753
+
715
754
  ---
716
755
 
717
756
  ## Troubleshooting
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.27.1",
34
+ version: "3.28.0",
35
35
  mcpName: "io.github.drjerryrelth/ghl-command",
36
36
  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.",
37
37
  main: "dist/index.js",
@@ -71,7 +71,7 @@ var require_package = __commonJS({
71
71
  "agency"
72
72
  ],
73
73
  author: "Elite DCs, LLC",
74
- license: "MIT",
74
+ license: "SEE LICENSE IN LICENSE",
75
75
  homepage: "https://elitedcs.com/ghl-mcp-server",
76
76
  repository: {
77
77
  type: "git",
@@ -1776,6 +1776,89 @@ ${errorBody}`
1776
1776
  }
1777
1777
  };
1778
1778
 
1779
+ // src/tools/tool-filter.ts
1780
+ var ALWAYS_ON_TOOL_NAMES = /* @__PURE__ */ new Set([
1781
+ "setup_ghl_mcp",
1782
+ "request_license",
1783
+ "get_mcp_version",
1784
+ "auto_capture_firebase_script",
1785
+ "enable_workflow_builder",
1786
+ "health_check"
1787
+ ]);
1788
+ function parseList(raw) {
1789
+ if (raw === void 0) return null;
1790
+ const items = raw.split(/[,\s]+/).map((x) => x.trim().toLowerCase()).filter((x) => x.length > 0);
1791
+ return items.length > 0 ? items : null;
1792
+ }
1793
+ function parseAllowlist(env) {
1794
+ const moduleList = parseList(env.GHL_ENABLED_MODULES);
1795
+ const toolList = parseList(env.GHL_ENABLED_TOOLS);
1796
+ return {
1797
+ enabledModules: moduleList ? new Set(moduleList) : null,
1798
+ enabledTools: toolList ? new Set(toolList) : null,
1799
+ rawModuleInput: moduleList,
1800
+ rawToolInput: toolList
1801
+ };
1802
+ }
1803
+ function isAllowlistActive(config3) {
1804
+ return config3.enabledModules !== null || config3.enabledTools !== null;
1805
+ }
1806
+ function shouldRegister(toolName, moduleName, config3) {
1807
+ if (ALWAYS_ON_TOOL_NAMES.has(toolName)) return true;
1808
+ if (!isAllowlistActive(config3)) return true;
1809
+ const toolLower = toolName.toLowerCase();
1810
+ const moduleLower = moduleName.toLowerCase();
1811
+ const moduleEnabled = config3.enabledModules?.has(moduleLower) ?? false;
1812
+ const toolEnabled = config3.enabledTools?.has(toolLower) ?? false;
1813
+ return moduleEnabled || toolEnabled;
1814
+ }
1815
+ function wrapServerForModule(realServer, moduleName, config3, attemptedTools, registeredTools) {
1816
+ return new Proxy(realServer, {
1817
+ get(target, prop, receiver) {
1818
+ if (prop === "tool") {
1819
+ return (name, ...rest) => {
1820
+ attemptedTools.add(name);
1821
+ if (!shouldRegister(name, moduleName, config3)) {
1822
+ return void 0;
1823
+ }
1824
+ registeredTools.add(name);
1825
+ return target.tool(name, ...rest);
1826
+ };
1827
+ }
1828
+ return Reflect.get(target, prop, receiver);
1829
+ }
1830
+ });
1831
+ }
1832
+ function validateAndLog(config3, knownModules, attemptedTools, registeredTools, log = (m) => process.stderr.write(m)) {
1833
+ if (!isAllowlistActive(config3)) return;
1834
+ if (config3.rawModuleInput) {
1835
+ const knownLower = new Set([...knownModules].map((m) => m.toLowerCase()));
1836
+ const unknown = config3.rawModuleInput.filter((m) => !knownLower.has(m));
1837
+ if (unknown.length > 0) {
1838
+ log(
1839
+ `[ghl-mcp] WARNING: GHL_ENABLED_MODULES has unrecognized name(s): ${unknown.join(", ")}. Known modules: ${[...knownModules].sort().join(", ")}.
1840
+ `
1841
+ );
1842
+ }
1843
+ }
1844
+ if (config3.rawToolInput) {
1845
+ const attemptedLower = new Set([...attemptedTools].map((t) => t.toLowerCase()));
1846
+ const unknown = config3.rawToolInput.filter((t) => !attemptedLower.has(t));
1847
+ if (unknown.length > 0) {
1848
+ log(
1849
+ `[ghl-mcp] WARNING: GHL_ENABLED_TOOLS has unrecognized name(s): ${unknown.join(", ")}.
1850
+ `
1851
+ );
1852
+ }
1853
+ }
1854
+ const moduleSummary = config3.enabledModules && config3.enabledModules.size > 0 ? `modules=[${[...config3.enabledModules].sort().join(",")}]` : "modules=(none)";
1855
+ const toolSummary = config3.enabledTools && config3.enabledTools.size > 0 ? `explicit-tools=[${[...config3.enabledTools].sort().join(",")}]` : "explicit-tools=(none)";
1856
+ log(
1857
+ `[ghl-mcp] Tool allowlist active: registered ${registeredTools.size} of ${attemptedTools.size} tools (${moduleSummary}; ${toolSummary}).
1858
+ `
1859
+ );
1860
+ }
1861
+
1779
1862
  // src/tools/contacts.ts
1780
1863
  var import_zod6 = require("zod");
1781
1864
 
@@ -8602,24 +8685,58 @@ var publicApiTools = [
8602
8685
  [registerAccountExportTools, "account-export"],
8603
8686
  [registerTemplateDeployerTools, "template-deployer"]
8604
8687
  ];
8605
- function registerAllTools(server2, client, registry2, mcpVersion) {
8606
- for (const [register] of publicApiTools) {
8607
- register(server2, client);
8688
+ var internalApiTools = [
8689
+ [registerWorkflowBuilderTools, "workflow-builder"],
8690
+ [registerFunnelBuilderTools, "funnel-builder"],
8691
+ [registerFormBuilderTools, "form-builder"],
8692
+ [registerPipelineBuilderTools, "pipeline-builder"],
8693
+ [registerWorkflowClonerTools, "workflow-cloner"],
8694
+ [registerSmartListTools, "smart-lists"],
8695
+ [registerReputationTools, "reputation"],
8696
+ [registerEmailCampaignTools, "email-campaigns"],
8697
+ [registerEmailBuilderInternalTools, "email-builder"],
8698
+ [registerMembershipTools, "memberships"]
8699
+ ];
8700
+ var VALIDATORS_MODULE = "validators";
8701
+ var DIAGNOSTICS_MODULE = "diagnostics";
8702
+ var LOCATION_SWITCHER_MODULE = "location-switcher";
8703
+ var KNOWN_MODULES = /* @__PURE__ */ new Set([
8704
+ ...publicApiTools.map(([, label]) => label),
8705
+ ...internalApiTools.map(([, label]) => label),
8706
+ VALIDATORS_MODULE,
8707
+ DIAGNOSTICS_MODULE,
8708
+ LOCATION_SWITCHER_MODULE
8709
+ ]);
8710
+ function registerAllTools(server2, client, registry2, mcpVersion, env = process.env) {
8711
+ const config3 = parseAllowlist(env);
8712
+ const attemptedTools = /* @__PURE__ */ new Set();
8713
+ const registeredTools = /* @__PURE__ */ new Set();
8714
+ const wrap = (moduleName) => wrapServerForModule(server2, moduleName, config3, attemptedTools, registeredTools);
8715
+ for (const [register, moduleName] of publicApiTools) {
8716
+ register(wrap(moduleName), client);
8608
8717
  }
8609
8718
  const builderClient = WorkflowBuilderClient.fromEnv(registry2) ?? WorkflowBuilderClient.fromFirstCompany(registry2);
8610
- registerWorkflowBuilderTools(server2, builderClient);
8611
- registerFunnelBuilderTools(server2, builderClient);
8612
- registerFormBuilderTools(server2, builderClient);
8613
- registerPipelineBuilderTools(server2, builderClient);
8614
- registerWorkflowClonerTools(server2, builderClient);
8615
- registerSmartListTools(server2, builderClient);
8616
- registerReputationTools(server2, builderClient);
8617
- registerEmailCampaignTools(server2, builderClient);
8618
- registerEmailBuilderInternalTools(server2, builderClient);
8619
- registerMembershipTools(server2, builderClient);
8620
- registerValidatorTools(server2, client, builderClient);
8621
- registerDiagnosticTools(server2, mcpVersion ?? "unknown", client, builderClient, registry2 ?? null);
8622
- registerLocationSwitcherTools(server2, client, builderClient, registry2, mcpVersion);
8719
+ for (const [register, moduleName] of internalApiTools) {
8720
+ register(wrap(moduleName), builderClient);
8721
+ }
8722
+ registerValidatorTools(wrap(VALIDATORS_MODULE), client, builderClient);
8723
+ registerDiagnosticTools(
8724
+ wrap(DIAGNOSTICS_MODULE),
8725
+ mcpVersion ?? "unknown",
8726
+ client,
8727
+ builderClient,
8728
+ registry2 ?? null
8729
+ );
8730
+ registerLocationSwitcherTools(
8731
+ wrap(LOCATION_SWITCHER_MODULE),
8732
+ client,
8733
+ builderClient,
8734
+ registry2,
8735
+ mcpVersion
8736
+ );
8737
+ if (isAllowlistActive(config3)) {
8738
+ validateAndLog(config3, KNOWN_MODULES, attemptedTools, registeredTools);
8739
+ }
8623
8740
  }
8624
8741
 
8625
8742
  // src/attestation.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.27.1",
3
+ "version": "3.28.0",
4
4
  "mcpName": "io.github.drjerryrelth/ghl-command",
5
5
  "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.",
6
6
  "main": "dist/index.js",
@@ -40,7 +40,7 @@
40
40
  "agency"
41
41
  ],
42
42
  "author": "Elite DCs, LLC",
43
- "license": "MIT",
43
+ "license": "SEE LICENSE IN LICENSE",
44
44
  "homepage": "https://elitedcs.com/ghl-mcp-server",
45
45
  "repository": {
46
46
  "type": "git",