@elitedcs/ghl-mcp 3.55.0 → 3.56.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 +27 -0
- package/README.md +36 -4
- package/dist/index.js +193 -49
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.56.0 — Security off-switches: any tool can be disabled; sub-account create/delete is opt-in
|
|
4
|
+
|
|
5
|
+
Shaped directly by subscriber feedback: some teams keep account-level
|
|
6
|
+
operations human-only, so a prompt-injected or misbehaving AI session can
|
|
7
|
+
never touch them. Two new controls, both enforced at server startup from
|
|
8
|
+
your config — there is deliberately NO way to change them from inside a
|
|
9
|
+
Claude conversation.
|
|
10
|
+
|
|
11
|
+
- **Sub-account create/delete is now OFF by default.** `create_sub_account`
|
|
12
|
+
and `delete_sub_account` register only when `GHL_ENABLE_ACCOUNT_ADMIN=1`
|
|
13
|
+
is set. The setup wizard asks (default No). The allowlist cannot bypass
|
|
14
|
+
the flag. If you were using these tools, add the flag and restart.
|
|
15
|
+
- **Universal deny-list**: `GHL_DISABLED_TOOLS` / `GHL_DISABLED_MODULES`
|
|
16
|
+
switch off any tool or module. A disabled tool never registers — it is
|
|
17
|
+
invisible to the model, so it can't be called *or discovered* from a
|
|
18
|
+
conversation. Deny wins over the allowlist and the always-on set,
|
|
19
|
+
including the credential helpers (`enable_workflow_builder`,
|
|
20
|
+
`auto_capture_firebase_script`, `capture_firebase_interactive`) and
|
|
21
|
+
`install_skills` (denying it also skips the bundled-skills auto-install).
|
|
22
|
+
- **Un-brickable**: the recovery core (`setup_ghl_mcp`, `request_license`,
|
|
23
|
+
`get_mcp_version`, `health_check`) ignores denial, with a logged warning.
|
|
24
|
+
- **Verifiable**: `health_check` gained a "Tool gating" section reporting
|
|
25
|
+
exactly what's disabled, whether account admin is on, and any misspelled
|
|
26
|
+
name in your gating vars (typos do NOT disable anything — they warn).
|
|
27
|
+
- Advertised default tool count is now 233 (235 with account admin enabled).
|
|
28
|
+
- README: new "Turning tools off (security)" section with examples.
|
|
29
|
+
|
|
3
30
|
## 3.55.0 — Skills ship inside the package: Blueprint installs itself
|
|
4
31
|
|
|
5
32
|
The gap between "the tools exist" and "the guided experience exists" is
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GHL Command — GoHighLevel MCP Server
|
|
2
2
|
|
|
3
|
-
**Full GoHighLevel API access for Claude.**
|
|
3
|
+
**Full GoHighLevel API access for Claude.** 233 tools across 49 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. **Multi-tenant:** one install can run the workflow builder across multiple clients' GHL accounts.
|
|
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
|
|
|
@@ -122,7 +122,7 @@ Run setup_ghl_mcp to activate GHL Command:
|
|
|
122
122
|
ghl_location_id: YOUR_LOCATION_ID
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
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** — the full core toolset is now unlocked (
|
|
125
|
+
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** — the full core toolset is now unlocked (233 tools total with the optional Workflow Builder Firebase add-on).
|
|
126
126
|
|
|
127
127
|
### 4. Try it
|
|
128
128
|
|
|
@@ -162,7 +162,7 @@ https://app.gohighlevel.com/v2/location/YOUR_LOCATION_ID/dashboard
|
|
|
162
162
|
|
|
163
163
|
## Enable the Workflow Builder
|
|
164
164
|
|
|
165
|
-
The builder + cloner + validator tools (workflow builder, funnel builder, form builder, pipeline builder, workflow cloner, validate_workflow) use GHL's internal API and require Firebase credentials. Without them, the other
|
|
165
|
+
The builder + cloner + validator tools (workflow builder, funnel builder, form builder, pipeline builder, workflow cloner, validate_workflow) use GHL's internal API and require Firebase credentials. Without them, the other 179 tools work fine — you just won't have workflow/funnel/form/pipeline editing. This is the flagship feature: do this step.
|
|
166
166
|
|
|
167
167
|
**One-click capture (v3.49.0+, the supported path):**
|
|
168
168
|
|
|
@@ -751,7 +751,7 @@ Source repo is private. Contributors need an invitation from `drjerryrelth`. The
|
|
|
751
751
|
|
|
752
752
|
### Reducing context / token usage
|
|
753
753
|
|
|
754
|
-
Every registered MCP tool's schema is shipped to the model on every message. With
|
|
754
|
+
Every registered MCP tool's schema is shipped to the model on every message. With 233 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`:
|
|
755
755
|
|
|
756
756
|
```jsonc
|
|
757
757
|
// Claude Desktop config — enable whole modules
|
|
@@ -786,6 +786,38 @@ Rules of the road:
|
|
|
786
786
|
|
|
787
787
|
`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`
|
|
788
788
|
|
|
789
|
+
### Turning tools off (security)
|
|
790
|
+
|
|
791
|
+
Some teams keep certain operations human-only — for example sub-account management or messaging — so that a prompt-injected or misbehaving AI session can never touch them. Two controls exist for exactly that:
|
|
792
|
+
|
|
793
|
+
**1. Sub-account create/delete is OFF by default.** `create_sub_account` and `delete_sub_account` do not register unless you explicitly opt in:
|
|
794
|
+
|
|
795
|
+
```jsonc
|
|
796
|
+
"env": {
|
|
797
|
+
"GHL_ENABLE_ACCOUNT_ADMIN": "1" // exactly "1" — nothing else enables it
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
The setup wizard asks this question during install (default No). Listing these tools in `GHL_ENABLED_TOOLS` does **not** bypass the flag.
|
|
802
|
+
|
|
803
|
+
**2. Any tool or module can be switched off** with a deny-list:
|
|
804
|
+
|
|
805
|
+
```jsonc
|
|
806
|
+
"env": {
|
|
807
|
+
"GHL_DISABLED_TOOLS": "send_message,bulk_delete_contacts",
|
|
808
|
+
"GHL_DISABLED_MODULES": "snapshots"
|
|
809
|
+
}
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
How the deny-list behaves:
|
|
813
|
+
|
|
814
|
+
- **Deny wins over everything** — the allowlist, the always-on set, and `GHL_ENABLE_ACCOUNT_ADMIN`. A denied tool **never registers**: it is invisible to the model, so it cannot be called *or discovered* from a conversation.
|
|
815
|
+
- **There is deliberately no tool to change gating from inside Claude.** Turning something back on requires editing your config file and fully restarting Claude — a human action.
|
|
816
|
+
- **Recovery core stays reachable:** `setup_ghl_mcp`, `request_license`, `get_mcp_version`, and `health_check` ignore denial (with a logged warning) so you can never brick your install. Everything else — including the Firebase credential helpers `enable_workflow_builder`, `auto_capture_firebase_script`, `capture_firebase_interactive`, and `install_skills` (denying it also skips the bundled-skills auto-install) — is deniable.
|
|
817
|
+
- **Verify it took effect:** run `health_check` in Claude. Its "Tool gating" section shows exactly what's disabled, whether account admin is on, and flags any misspelled name in your gating vars (typos do NOT disable anything — they warn instead).
|
|
818
|
+
- Single-location shops that never want cross-account movement can also deny `switch_location` and `register_location`.
|
|
819
|
+
- Honest limit: this gate is enforced at server startup from your config file, so it is as strong as your machine's file permissions. What it guarantees is that nothing *inside a Claude conversation* can widen the tool surface.
|
|
820
|
+
|
|
789
821
|
---
|
|
790
822
|
|
|
791
823
|
## Troubleshooting
|
package/dist/index.js
CHANGED
|
@@ -1235,7 +1235,7 @@ async function validateFirebase(firebaseKey, refreshToken) {
|
|
|
1235
1235
|
function registerSetupTool(server2) {
|
|
1236
1236
|
server2.tool(
|
|
1237
1237
|
"setup_ghl_mcp",
|
|
1238
|
-
"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
|
|
1238
|
+
"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 233 tools (179 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
|
|
1239
1239
|
{
|
|
1240
1240
|
email: import_zod40.z.string().email().describe("Email used at purchase."),
|
|
1241
1241
|
license_key: import_zod40.z.string().min(20).describe("License key from your purchase email."),
|
|
@@ -1317,7 +1317,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
|
|
|
1317
1317
|
signed_attestation: lic.signedAttestation
|
|
1318
1318
|
});
|
|
1319
1319
|
const isFree = lic.tier === "free";
|
|
1320
|
-
const toolCount = isFree ? workflowBuilderEnabled ? "107" : "89" : workflowBuilderEnabled ? "
|
|
1320
|
+
const toolCount = isFree ? workflowBuilderEnabled ? "107" : "89" : workflowBuilderEnabled ? "233" : "179";
|
|
1321
1321
|
const wfLine = workflowBuilderEnabled ? "Workflow Builder: enabled." : "Workflow Builder: not configured (optional).";
|
|
1322
1322
|
const wfTip = workflowBuilderEnabled ? "" : isFree ? `
|
|
1323
1323
|
Unlock the account auditor next (the free tier's best tool): say "Unlock the Workflow Builder" \u2014 one browser login enables audit_workflows, validate_workflow, and the full-detail reads.` : "\nTo enable Workflow Builder later (52 extra Firebase-gated tools): run enable_workflow_builder with your three Firebase values. No need to re-enter license/API key/location ID.";
|
|
@@ -1347,7 +1347,7 @@ Unlock the account auditor next (the free tier's best tool): say "Unlock the Wor
|
|
|
1347
1347
|
function registerEnableWorkflowBuilderTool(server2) {
|
|
1348
1348
|
server2.tool(
|
|
1349
1349
|
"enable_workflow_builder",
|
|
1350
|
-
"Add Firebase credentials to an existing GHL Command install to unlock 54 additional tools across the internal-API modules: workflow builder (create/edit/clone/delete/publish/validate workflows, build_if_else_branch, build_goal_event, get_trigger_registry), funnel + page builder, form builder, pipeline builder, workflow cloner, smart lists, reputation, email campaigns, email templates, and memberships, plus the pre-deploy validator. On the FREE tier this same login unlocks the read-only auditor suite (audit_workflows, validate_workflow, full-detail workflow/funnel/pipeline reads). Requires you've already run setup_ghl_mcp. EASIEST PATH: run `capture_firebase_interactive` instead \u2014 a Chrome window opens, you log into GHL, zero pasting. Use THIS tool when you have JSON from `auto_capture_firebase_script` (console-paste path) to put in `firebase_paste`, or the three manual DevTools fields. Tool count goes from
|
|
1350
|
+
"Add Firebase credentials to an existing GHL Command install to unlock 54 additional tools across the internal-API modules: workflow builder (create/edit/clone/delete/publish/validate workflows, build_if_else_branch, build_goal_event, get_trigger_registry), funnel + page builder, form builder, pipeline builder, workflow cloner, smart lists, reputation, email campaigns, email templates, and memberships, plus the pre-deploy validator. On the FREE tier this same login unlocks the read-only auditor suite (audit_workflows, validate_workflow, full-detail workflow/funnel/pipeline reads). Requires you've already run setup_ghl_mcp. EASIEST PATH: run `capture_firebase_interactive` instead \u2014 a Chrome window opens, you log into GHL, zero pasting. Use THIS tool when you have JSON from `auto_capture_firebase_script` (console-paste path) to put in `firebase_paste`, or the three manual DevTools fields. Tool count goes from 179 to 233 after the next Claude restart.",
|
|
1351
1351
|
{
|
|
1352
1352
|
// v3.25.0: one-paste path. Tool runs `auto_capture_firebase_script` to
|
|
1353
1353
|
// get the console script; the script returns a JSON object that pastes
|
|
@@ -1428,7 +1428,7 @@ DevTools steps: https://elitedcs.com/ghl-mcp-firebase`
|
|
|
1428
1428
|
"",
|
|
1429
1429
|
"**You MUST restart Claude before using any workflow-builder tool.** Quit Claude completely (Cmd+Q on Mac, full exit on Windows) and reopen. Without a restart, the workflow builder tools will keep using the OLD Firebase auth from before this call and fail with 401 errors \u2014 even though this tool reported success.",
|
|
1430
1430
|
"",
|
|
1431
|
-
'After restart, all
|
|
1431
|
+
'After restart, all 233 tools load. Try: "List my workflows in full detail" or "Validate workflow <id>".',
|
|
1432
1432
|
"",
|
|
1433
1433
|
"Note: Firebase refresh tokens rotate every few weeks. If workflow tools stop working in a few weeks (run `health_check` to confirm Firebase auth: FAIL), run `auto_capture_firebase_script` for fresh values and re-run this tool with the new firebase_paste."
|
|
1434
1434
|
].join("\n")
|
|
@@ -1524,7 +1524,7 @@ The login in the capture window may belong to the wrong GHL account, or the sess
|
|
|
1524
1524
|
"",
|
|
1525
1525
|
"**You MUST restart Claude before using any workflow-builder tool.** Quit Claude completely (Cmd+Q on Mac, full exit on Windows) and reopen.",
|
|
1526
1526
|
"",
|
|
1527
|
-
'After restart, all
|
|
1527
|
+
'After restart, all 233 tools load. Try: "List my workflows in full detail".',
|
|
1528
1528
|
"",
|
|
1529
1529
|
"Future token rotations re-capture silently \u2014 if workflow tools ever 401, just run capture_firebase_interactive again; no window should appear."
|
|
1530
1530
|
].join("\n")
|
|
@@ -2102,9 +2102,9 @@ var require_package = __commonJS({
|
|
|
2102
2102
|
"package.json"(exports2, module2) {
|
|
2103
2103
|
module2.exports = {
|
|
2104
2104
|
name: "@elitedcs/ghl-mcp",
|
|
2105
|
-
version: "3.
|
|
2105
|
+
version: "3.56.0",
|
|
2106
2106
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
2107
|
-
description: "GoHighLevel MCP Server for Claude.
|
|
2107
|
+
description: "GoHighLevel MCP Server for Claude. 233 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
2108
2108
|
main: "dist/index.js",
|
|
2109
2109
|
bin: {
|
|
2110
2110
|
"ghl-mcp": "dist/index.js"
|
|
@@ -5683,6 +5683,25 @@ var ALWAYS_ON_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
5683
5683
|
"enable_workflow_builder",
|
|
5684
5684
|
"health_check"
|
|
5685
5685
|
]);
|
|
5686
|
+
var HARD_CORE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
5687
|
+
"setup_ghl_mcp",
|
|
5688
|
+
"request_license",
|
|
5689
|
+
"get_mcp_version",
|
|
5690
|
+
"health_check"
|
|
5691
|
+
]);
|
|
5692
|
+
var ACCOUNT_ADMIN_TOOLS = /* @__PURE__ */ new Set([
|
|
5693
|
+
"create_sub_account",
|
|
5694
|
+
"delete_sub_account"
|
|
5695
|
+
]);
|
|
5696
|
+
var OUT_OF_BAND_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
5697
|
+
"setup_ghl_mcp",
|
|
5698
|
+
"request_license",
|
|
5699
|
+
"get_mcp_version",
|
|
5700
|
+
"enable_workflow_builder",
|
|
5701
|
+
"auto_capture_firebase_script",
|
|
5702
|
+
"capture_firebase_interactive",
|
|
5703
|
+
"install_skills"
|
|
5704
|
+
]);
|
|
5686
5705
|
var FREE_TIER_EXTRA_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
5687
5706
|
"audit_workflows",
|
|
5688
5707
|
// account-wide silent-failure audit — the free tier's wow moment
|
|
@@ -5732,17 +5751,39 @@ function parseList(raw) {
|
|
|
5732
5751
|
function parseAllowlist(env) {
|
|
5733
5752
|
const moduleList = parseList(env.GHL_ENABLED_MODULES);
|
|
5734
5753
|
const toolList = parseList(env.GHL_ENABLED_TOOLS);
|
|
5754
|
+
const disabledModuleList = parseList(env.GHL_DISABLED_MODULES);
|
|
5755
|
+
const disabledToolList = parseList(env.GHL_DISABLED_TOOLS);
|
|
5735
5756
|
return {
|
|
5736
5757
|
enabledModules: moduleList ? new Set(moduleList) : null,
|
|
5737
5758
|
enabledTools: toolList ? new Set(toolList) : null,
|
|
5759
|
+
disabledModules: disabledModuleList ? new Set(disabledModuleList) : null,
|
|
5760
|
+
disabledTools: disabledToolList ? new Set(disabledToolList) : null,
|
|
5761
|
+
accountAdminEnabled: env.GHL_ENABLE_ACCOUNT_ADMIN === "1",
|
|
5738
5762
|
rawModuleInput: moduleList,
|
|
5739
|
-
rawToolInput: toolList
|
|
5763
|
+
rawToolInput: toolList,
|
|
5764
|
+
rawDisabledModuleInput: disabledModuleList,
|
|
5765
|
+
rawDisabledToolInput: disabledToolList
|
|
5740
5766
|
};
|
|
5741
5767
|
}
|
|
5742
5768
|
function isAllowlistActive(config3) {
|
|
5743
5769
|
return config3.enabledModules !== null || config3.enabledTools !== null;
|
|
5744
5770
|
}
|
|
5771
|
+
function isDenyListActive(config3) {
|
|
5772
|
+
return config3.disabledModules !== null || config3.disabledTools !== null;
|
|
5773
|
+
}
|
|
5774
|
+
function isGatingActive(config3) {
|
|
5775
|
+
return isAllowlistActive(config3) || isDenyListActive(config3) || config3.accountAdminEnabled;
|
|
5776
|
+
}
|
|
5777
|
+
function isToolDenied(toolName, moduleName, config3) {
|
|
5778
|
+
if (HARD_CORE_TOOL_NAMES.has(toolName)) return false;
|
|
5779
|
+
const toolDenied = config3.disabledTools?.has(toolName.toLowerCase()) ?? false;
|
|
5780
|
+
const moduleDenied = moduleName.length > 0 && (config3.disabledModules?.has(moduleName.toLowerCase()) ?? false);
|
|
5781
|
+
return toolDenied || moduleDenied;
|
|
5782
|
+
}
|
|
5745
5783
|
function shouldRegister(toolName, moduleName, config3) {
|
|
5784
|
+
if (HARD_CORE_TOOL_NAMES.has(toolName)) return true;
|
|
5785
|
+
if (isToolDenied(toolName, moduleName, config3)) return false;
|
|
5786
|
+
if (ACCOUNT_ADMIN_TOOLS.has(toolName) && !config3.accountAdminEnabled) return false;
|
|
5746
5787
|
if (ALWAYS_ON_TOOL_NAMES.has(toolName)) return true;
|
|
5747
5788
|
if (!isAllowlistActive(config3)) return true;
|
|
5748
5789
|
const toolLower = toolName.toLowerCase();
|
|
@@ -5782,32 +5823,57 @@ function wrapServerForModule(realServer, moduleName, config3, attemptedTools, re
|
|
|
5782
5823
|
}
|
|
5783
5824
|
});
|
|
5784
5825
|
}
|
|
5826
|
+
function buildGatingReport(config3, knownModules, attemptedTools, registeredTools, outOfBand) {
|
|
5827
|
+
const knownLower = new Set([...knownModules].map((m) => m.toLowerCase()));
|
|
5828
|
+
const knownToolsLower = /* @__PURE__ */ new Set([
|
|
5829
|
+
...[...attemptedTools].map((t) => t.toLowerCase()),
|
|
5830
|
+
...[...OUT_OF_BAND_TOOL_NAMES].map((t) => t.toLowerCase())
|
|
5831
|
+
]);
|
|
5832
|
+
const unknownModules = (raw) => (raw ?? []).filter((m) => !knownLower.has(m));
|
|
5833
|
+
const unknownTools = (raw) => (raw ?? []).filter((t) => !knownToolsLower.has(t));
|
|
5834
|
+
const hardCoreLower = new Set([...HARD_CORE_TOOL_NAMES].map((t) => t.toLowerCase()));
|
|
5835
|
+
return {
|
|
5836
|
+
gatingActive: isGatingActive(config3),
|
|
5837
|
+
allowlistActive: isAllowlistActive(config3),
|
|
5838
|
+
denyListActive: isDenyListActive(config3),
|
|
5839
|
+
accountAdminEnabled: config3.accountAdminEnabled,
|
|
5840
|
+
enabledModules: [...config3.enabledModules ?? []].sort(),
|
|
5841
|
+
enabledTools: [...config3.enabledTools ?? []].sort(),
|
|
5842
|
+
disabledModules: [...config3.disabledModules ?? []].sort(),
|
|
5843
|
+
disabledTools: [...config3.disabledTools ?? []].sort(),
|
|
5844
|
+
ignoredHardCoreDenials: (config3.rawDisabledToolInput ?? []).filter((t) => hardCoreLower.has(t)),
|
|
5845
|
+
unknownEnabledModules: unknownModules(config3.rawModuleInput),
|
|
5846
|
+
unknownEnabledTools: unknownTools(config3.rawToolInput),
|
|
5847
|
+
unknownDisabledModules: unknownModules(config3.rawDisabledModuleInput),
|
|
5848
|
+
unknownDisabledTools: unknownTools(config3.rawDisabledToolInput),
|
|
5849
|
+
outOfBandDenied: [...outOfBand?.denied ?? []].sort(),
|
|
5850
|
+
registeredCount: registeredTools.size + (outOfBand?.registered.size ?? 0),
|
|
5851
|
+
attemptedCount: attemptedTools.size + (outOfBand?.registered.size ?? 0) + (outOfBand?.denied.size ?? 0)
|
|
5852
|
+
};
|
|
5853
|
+
}
|
|
5785
5854
|
function validateAndLog(config3, knownModules, attemptedTools, registeredTools, log = (m) => process.stderr.write(m)) {
|
|
5786
|
-
if (!
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
`[ghl-mcp] WARNING: GHL_ENABLED_MODULES has unrecognized name(s): ${unknown.join(", ")}. Known modules: ${[...knownModules].sort().join(", ")}.
|
|
5793
|
-
`
|
|
5794
|
-
);
|
|
5855
|
+
if (!isGatingActive(config3)) return;
|
|
5856
|
+
const report = buildGatingReport(config3, knownModules, attemptedTools, registeredTools);
|
|
5857
|
+
const warnUnknown = (varName, names, hint = "") => {
|
|
5858
|
+
if (names.length > 0) {
|
|
5859
|
+
log(`[ghl-mcp] WARNING: ${varName} has unrecognized name(s): ${names.join(", ")}.${hint}
|
|
5860
|
+
`);
|
|
5795
5861
|
}
|
|
5796
|
-
}
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5862
|
+
};
|
|
5863
|
+
const modulesHint = ` Known modules: ${[...knownModules].sort().join(", ")}.`;
|
|
5864
|
+
warnUnknown("GHL_ENABLED_MODULES", report.unknownEnabledModules, modulesHint);
|
|
5865
|
+
warnUnknown("GHL_ENABLED_TOOLS", report.unknownEnabledTools);
|
|
5866
|
+
warnUnknown("GHL_DISABLED_MODULES", report.unknownDisabledModules, modulesHint);
|
|
5867
|
+
warnUnknown("GHL_DISABLED_TOOLS", report.unknownDisabledTools);
|
|
5868
|
+
if (report.ignoredHardCoreDenials.length > 0) {
|
|
5869
|
+
log(
|
|
5870
|
+
`[ghl-mcp] WARNING: GHL_DISABLED_TOOLS lists recovery-core tool(s) that cannot be disabled (ignored): ${report.ignoredHardCoreDenials.join(", ")}.
|
|
5803
5871
|
`
|
|
5804
|
-
|
|
5805
|
-
}
|
|
5872
|
+
);
|
|
5806
5873
|
}
|
|
5807
|
-
const
|
|
5808
|
-
const toolSummary = config3.enabledTools && config3.enabledTools.size > 0 ? `explicit-tools=[${[...config3.enabledTools].sort().join(",")}]` : "explicit-tools=(none)";
|
|
5874
|
+
const listOr = (label, items) => items.length > 0 ? `${label}=[${items.join(",")}]` : `${label}=(none)`;
|
|
5809
5875
|
log(
|
|
5810
|
-
`[ghl-mcp] Tool
|
|
5876
|
+
`[ghl-mcp] Tool gating active: registered ${registeredTools.size} of ${attemptedTools.size} tools (${listOr("modules", report.enabledModules)}; ${listOr("explicit-tools", report.enabledTools)}; ${listOr("disabled-modules", report.disabledModules)}; ${listOr("disabled-tools", report.disabledTools)}; account-admin=${report.accountAdminEnabled ? "ENABLED" : "off (default)"}).
|
|
5811
5877
|
`
|
|
5812
5878
|
);
|
|
5813
5879
|
}
|
|
@@ -14083,10 +14149,10 @@ async function getVersionStatus(installed) {
|
|
|
14083
14149
|
}
|
|
14084
14150
|
|
|
14085
14151
|
// src/tools/diagnostics.ts
|
|
14086
|
-
function registerDiagnosticTools(server2, installedVersion, client, builderClient, registry2) {
|
|
14152
|
+
function registerDiagnosticTools(server2, installedVersion, client, builderClient, registry2, getGatingReport) {
|
|
14087
14153
|
server2.tool(
|
|
14088
14154
|
"health_check",
|
|
14089
|
-
"Run a full health check of the GHL Command MCP install. Reports on: npm registry + version status, GHL API key validity, default location reachability, Firebase auth status (workflow builder), and
|
|
14155
|
+
"Run a full health check of the GHL Command MCP install. Reports on: npm registry + version status, GHL API key validity, default location reachability, Firebase auth status (workflow builder), token registry, and the tool-gating policy (disabled tools/modules, account-admin opt-in, typos in gating env vars). Returns a structured pass/fail/warn list with detail per check. Use this when something feels broken or after credentials change.",
|
|
14090
14156
|
{},
|
|
14091
14157
|
async () => {
|
|
14092
14158
|
const checks = [];
|
|
@@ -14136,7 +14202,7 @@ function registerDiagnosticTools(server2, installedVersion, client, builderClien
|
|
|
14136
14202
|
})();
|
|
14137
14203
|
const firebasePromise = (async () => {
|
|
14138
14204
|
if (!builderClient) {
|
|
14139
|
-
return { name: "Firebase auth (workflow builder)", status: "skip", detail: "Not configured. The
|
|
14205
|
+
return { name: "Firebase auth (workflow builder)", status: "skip", detail: "Not configured. The 54 Firebase-gated tools need Firebase credentials. The other 179 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." };
|
|
14140
14206
|
}
|
|
14141
14207
|
const result = await builderClient.checkAuth();
|
|
14142
14208
|
const tokenCompany = builderClient.getTokenCompanyId();
|
|
@@ -14185,7 +14251,32 @@ function registerDiagnosticTools(server2, installedVersion, client, builderClien
|
|
|
14185
14251
|
} else {
|
|
14186
14252
|
versionCheck = { name: "npm registry + version", status: "warn", detail: `Installed v${versionStatus.installed} but latest is v${versionStatus.latest}. Quit Claude (Cmd+Q) and reopen to upgrade.` };
|
|
14187
14253
|
}
|
|
14188
|
-
|
|
14254
|
+
const gatingCheck = (() => {
|
|
14255
|
+
const r = getGatingReport?.();
|
|
14256
|
+
if (!r) {
|
|
14257
|
+
return { name: "Tool gating", status: "skip", detail: "Gating report unavailable in this mode." };
|
|
14258
|
+
}
|
|
14259
|
+
if (!r.gatingActive) {
|
|
14260
|
+
return { name: "Tool gating", status: "pass", detail: `No gating configured \u2014 all ${r.registeredCount} tools registered. Sub-account create/delete stays OFF unless GHL_ENABLE_ACCOUNT_ADMIN=1 is set.` };
|
|
14261
|
+
}
|
|
14262
|
+
const parts = [`${r.registeredCount} of ${r.attemptedCount} tools registered.`];
|
|
14263
|
+
if (r.disabledTools.length > 0) parts.push(`Disabled tools: ${r.disabledTools.join(", ")}.`);
|
|
14264
|
+
if (r.disabledModules.length > 0) parts.push(`Disabled modules: ${r.disabledModules.join(", ")}.`);
|
|
14265
|
+
if (r.outOfBandDenied.length > 0) parts.push(`Confirmed unregistered (setup/helper tools): ${r.outOfBandDenied.join(", ")}.`);
|
|
14266
|
+
if (r.allowlistActive) parts.push(`Allowlist: modules=[${r.enabledModules.join(",") || "-"}], tools=[${r.enabledTools.join(",") || "-"}].`);
|
|
14267
|
+
parts.push(`Account admin (sub-account create/delete): ${r.accountAdminEnabled ? "ENABLED via GHL_ENABLE_ACCOUNT_ADMIN=1" : "OFF (default)"}.`);
|
|
14268
|
+
const problems = [];
|
|
14269
|
+
if (r.ignoredHardCoreDenials.length > 0) problems.push(`recovery-core tool(s) in GHL_DISABLED_TOOLS cannot be disabled and were IGNORED: ${r.ignoredHardCoreDenials.join(", ")}`);
|
|
14270
|
+
if (r.unknownDisabledTools.length > 0) problems.push(`unrecognized name(s) in GHL_DISABLED_TOOLS (possible typo \u2014 those tools are NOT disabled): ${r.unknownDisabledTools.join(", ")}`);
|
|
14271
|
+
if (r.unknownDisabledModules.length > 0) problems.push(`unrecognized name(s) in GHL_DISABLED_MODULES (possible typo): ${r.unknownDisabledModules.join(", ")}`);
|
|
14272
|
+
if (r.unknownEnabledTools.length > 0) problems.push(`unrecognized name(s) in GHL_ENABLED_TOOLS: ${r.unknownEnabledTools.join(", ")}`);
|
|
14273
|
+
if (r.unknownEnabledModules.length > 0) problems.push(`unrecognized name(s) in GHL_ENABLED_MODULES: ${r.unknownEnabledModules.join(", ")}`);
|
|
14274
|
+
if (problems.length > 0) {
|
|
14275
|
+
return { name: "Tool gating", status: "warn", detail: `${parts.join(" ")} PROBLEMS: ${problems.join("; ")}. Fix the env var in your Claude config and fully restart Claude.` };
|
|
14276
|
+
}
|
|
14277
|
+
return { name: "Tool gating", status: "pass", detail: parts.join(" ") };
|
|
14278
|
+
})();
|
|
14279
|
+
checks.push(versionCheck, apiKeyCheck, locationCheck, firebaseCheck, registryCheck, gatingCheck);
|
|
14189
14280
|
const symbols = { pass: "\u2713", fail: "\u2717", warn: "!", skip: "\u2014" };
|
|
14190
14281
|
const lines = [];
|
|
14191
14282
|
lines.push("GHL Command \u2014 Health Check");
|
|
@@ -17677,7 +17768,7 @@ var KNOWN_MODULES = /* @__PURE__ */ new Set([
|
|
|
17677
17768
|
LOCATION_SWITCHER_MODULE,
|
|
17678
17769
|
SNAPSHOTS_MODULE
|
|
17679
17770
|
]);
|
|
17680
|
-
function registerAllTools(server2, client, registry2, mcpVersion, env = process.env, tier = "full") {
|
|
17771
|
+
function registerAllTools(server2, client, registry2, mcpVersion, env = process.env, tier = "full", outOfBand) {
|
|
17681
17772
|
const config3 = parseAllowlist(env);
|
|
17682
17773
|
const attemptedTools = /* @__PURE__ */ new Set();
|
|
17683
17774
|
const registeredTools = /* @__PURE__ */ new Set();
|
|
@@ -17699,7 +17790,8 @@ function registerAllTools(server2, client, registry2, mcpVersion, env = process.
|
|
|
17699
17790
|
mcpVersion ?? "unknown",
|
|
17700
17791
|
client,
|
|
17701
17792
|
builderClient,
|
|
17702
|
-
registry2 ?? null
|
|
17793
|
+
registry2 ?? null,
|
|
17794
|
+
() => buildGatingReport(config3, KNOWN_MODULES, attemptedTools, registeredTools, outOfBand)
|
|
17703
17795
|
);
|
|
17704
17796
|
registerSnapshotTools(wrap(SNAPSHOTS_MODULE), client, registry2);
|
|
17705
17797
|
registerLocationSwitcherTools(
|
|
@@ -17709,7 +17801,7 @@ function registerAllTools(server2, client, registry2, mcpVersion, env = process.
|
|
|
17709
17801
|
registry2,
|
|
17710
17802
|
mcpVersion
|
|
17711
17803
|
);
|
|
17712
|
-
if (
|
|
17804
|
+
if (isGatingActive(config3)) {
|
|
17713
17805
|
validateAndLog(config3, KNOWN_MODULES, attemptedTools, registeredTools);
|
|
17714
17806
|
}
|
|
17715
17807
|
if (tier === "free") {
|
|
@@ -17721,6 +17813,26 @@ function registerAllTools(server2, client, registry2, mcpVersion, env = process.
|
|
|
17721
17813
|
return { registeredTools, gatedTools };
|
|
17722
17814
|
}
|
|
17723
17815
|
|
|
17816
|
+
// src/out-of-band.ts
|
|
17817
|
+
function makeOutOfBandTracker() {
|
|
17818
|
+
return { registered: /* @__PURE__ */ new Set(), denied: /* @__PURE__ */ new Set() };
|
|
17819
|
+
}
|
|
17820
|
+
function registerOutOfBand(entries, config3, tracker, log = (m) => process.stderr.write(m)) {
|
|
17821
|
+
for (const entry of entries) {
|
|
17822
|
+
if (isToolDenied(entry.name, "", config3)) {
|
|
17823
|
+
tracker.denied.add(entry.name);
|
|
17824
|
+
log(
|
|
17825
|
+
`[ghl-mcp] Tool disabled by GHL_DISABLED_TOOLS: ${entry.name}${entry.deniedNote ? ` (${entry.deniedNote})` : ""}
|
|
17826
|
+
`
|
|
17827
|
+
);
|
|
17828
|
+
continue;
|
|
17829
|
+
}
|
|
17830
|
+
tracker.registered.add(entry.name);
|
|
17831
|
+
entry.register();
|
|
17832
|
+
entry.sideEffect?.();
|
|
17833
|
+
}
|
|
17834
|
+
}
|
|
17835
|
+
|
|
17724
17836
|
// src/index.ts
|
|
17725
17837
|
init_credentials_store();
|
|
17726
17838
|
init_setup_tool();
|
|
@@ -18408,6 +18520,9 @@ async function resolveAccessAndRegister() {
|
|
|
18408
18520
|
`);
|
|
18409
18521
|
}
|
|
18410
18522
|
inBootstrapMode = !apiKey || !locationId || !licenseVerified;
|
|
18523
|
+
const gatingConfig = parseAllowlist(process.env);
|
|
18524
|
+
const outOfBandTracker = makeOutOfBandTracker();
|
|
18525
|
+
outOfBandTracker.registered.add("get_mcp_version");
|
|
18411
18526
|
if (inBootstrapMode) {
|
|
18412
18527
|
process.stderr.write(
|
|
18413
18528
|
`[ghl-mcp] Bootstrap mode.
|
|
@@ -18417,23 +18532,52 @@ async function resolveAccessAndRegister() {
|
|
|
18417
18532
|
);
|
|
18418
18533
|
registerSetupTool(server);
|
|
18419
18534
|
registerLeadCaptureTool(server);
|
|
18420
|
-
|
|
18535
|
+
registerOutOfBand(
|
|
18536
|
+
[{ name: "auto_capture_firebase_script", register: () => registerFirebaseCaptureScriptTool(server) }],
|
|
18537
|
+
gatingConfig,
|
|
18538
|
+
outOfBandTracker
|
|
18539
|
+
);
|
|
18421
18540
|
} else {
|
|
18422
18541
|
const client = new GHLClient({ apiKey, locationId });
|
|
18423
|
-
registerAllTools(server, client, registry, pkg.version, process.env, sessionTier);
|
|
18424
|
-
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
18431
|
-
|
|
18542
|
+
registerAllTools(server, client, registry, pkg.version, process.env, sessionTier, outOfBandTracker);
|
|
18543
|
+
registerOutOfBand(
|
|
18544
|
+
[
|
|
18545
|
+
// Available in normal mode so buyers can add Firebase later without
|
|
18546
|
+
// re-running setup_ghl_mcp with all 7 fields, and so they can re-grab
|
|
18547
|
+
// a fresh refresh token when it rotates. Registered on the FREE tier too:
|
|
18548
|
+
// the Firebase login powers the read-only auditor tools (audit_workflows,
|
|
18549
|
+
// validate_workflow, *_full reads) — only the write tools stay gated.
|
|
18550
|
+
{ name: "enable_workflow_builder", register: () => registerEnableWorkflowBuilderTool(server) },
|
|
18551
|
+
{ name: "auto_capture_firebase_script", register: () => registerFirebaseCaptureScriptTool(server) },
|
|
18552
|
+
// One-click Builder unlock — buyer just logs into a Chrome window the
|
|
18553
|
+
// helper opens; also the silent re-capture path for token rotations.
|
|
18554
|
+
{ name: "capture_firebase_interactive", register: () => registerInteractiveCaptureTool(server) },
|
|
18555
|
+
// Skills rail (2026-07-28): bundled skills (Blueprint) install themselves
|
|
18556
|
+
// into ~/.claude/skills — subscribers never download anything separately.
|
|
18557
|
+
// Auto-runs on every start (idempotent, never clobbers user edits);
|
|
18558
|
+
// the tool exists for verification/repair. Every tier: local file copy
|
|
18559
|
+
// only. Denying install_skills also skips the auto-install — a buyer who
|
|
18560
|
+
// gated it clearly doesn't want this server writing files into ~/.claude.
|
|
18561
|
+
{
|
|
18562
|
+
name: "install_skills",
|
|
18563
|
+
register: () => registerSkillsTool(server, pkg.version, __dirname),
|
|
18564
|
+
deniedNote: "bundled-skills auto-install skipped too",
|
|
18565
|
+
sideEffect: () => {
|
|
18566
|
+
try {
|
|
18567
|
+
const skillsResult = installBundledSkills({ packageVersion: pkg.version, baseDir: __dirname });
|
|
18568
|
+
const line = summarizeInstall(skillsResult);
|
|
18569
|
+
if (line) process.stderr.write(`[ghl-mcp] ${line}
|
|
18432
18570
|
`);
|
|
18433
|
-
|
|
18434
|
-
|
|
18571
|
+
} catch (e) {
|
|
18572
|
+
process.stderr.write(`[ghl-mcp] Skills install skipped: ${String(e).slice(0, 120)}
|
|
18435
18573
|
`);
|
|
18436
|
-
|
|
18574
|
+
}
|
|
18575
|
+
}
|
|
18576
|
+
}
|
|
18577
|
+
],
|
|
18578
|
+
gatingConfig,
|
|
18579
|
+
outOfBandTracker
|
|
18580
|
+
);
|
|
18437
18581
|
if (fileCreds && !process.env.GHL_API_KEY) {
|
|
18438
18582
|
process.stderr.write(`[ghl-mcp] Loaded credentials from ${credentialsPath()}
|
|
18439
18583
|
`);
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.56.0",
|
|
4
4
|
"mcpName": "io.github.drjerryrelth/ghl-command",
|
|
5
|
-
"description": "GoHighLevel MCP Server for Claude.
|
|
5
|
+
"description": "GoHighLevel MCP Server for Claude. 233 tools — full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"ghl-mcp": "dist/index.js"
|