@elitedcs/ghl-mcp 3.27.2 → 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,44 @@
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
+
3
42
  ## 3.27.2 — Fix license metadata: MIT → Proprietary
4
43
 
5
44
  Metadata-only release. Corrects the npm package's stated license, which
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.2",
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",
@@ -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.2",
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",