@elitedcs/ghl-mcp 3.7.0 → 3.8.1
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 +100 -0
- package/README.md +2 -2
- package/dist/index.js +102 -29
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,105 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.8.1 — Vitest test suite + pre-publish gate
|
|
4
|
+
|
|
5
|
+
**178 tools across 38 modules. Bundle: 291.3 KB (unchanged).**
|
|
6
|
+
|
|
7
|
+
Strict safety patch. No new features, no behavior changes — just tests covering the high-risk code paths so regressions get caught at build time instead of in production.
|
|
8
|
+
|
|
9
|
+
### New: Vitest test suite (87 tests, 154ms)
|
|
10
|
+
Four test files added under `src/`:
|
|
11
|
+
|
|
12
|
+
- `retry.test.ts` (15 tests) — `computeRetryDelay`: Retry-After delta-seconds + HTTP-date parsing + clamping + full-jitter backoff math. Catches the "5junk" mixed-string regression and the "doubles per attempt" backoff guarantee.
|
|
13
|
+
- `trigger-schemas.test.ts` (60 tests) — Every one of the 57 native trigger types parses through its typed variant. Unknown future types fall through to the permissive fallback. Real-world `form_submission` payload preserves passthrough fields.
|
|
14
|
+
- `workflow-builder-client.test.ts` (7 tests) — `normalizeRemoveFromWorkflowAction` covers all branches (no-op when both fields present, synthesize string from array, synthesize array from string, multi-element array picks first, empty array no-op, non-target action type no-op, missing attributes no-op).
|
|
15
|
+
- `version-check.test.ts` (5 + tests) — `fetchLatestVersion` + `getVersionStatus` with mocked `fetch`: 200/non-2xx/network-error/missing-version-field/non-string-version cases.
|
|
16
|
+
|
|
17
|
+
### CI gate
|
|
18
|
+
The npm publish workflow now runs `npm test` before the tag-vs-version check. **Tests must pass for a release to ship.** Combined with the post-publish smoke test that verifies the package boots cleanly on a fresh machine, both surfaces are now covered:
|
|
19
|
+
|
|
20
|
+
- Pre-publish: schema correctness, normalization logic, retry math
|
|
21
|
+
- Post-publish: server boots, tool registry includes required tools
|
|
22
|
+
|
|
23
|
+
### New npm scripts
|
|
24
|
+
- `npm test` — run once, exit
|
|
25
|
+
- `npm run test:watch` — re-run on file change during development
|
|
26
|
+
|
|
27
|
+
### Dev-only dep added
|
|
28
|
+
- `vitest@^4.1.6` (devDependencies — not shipped to consumers; the npm tarball is unchanged size)
|
|
29
|
+
|
|
30
|
+
### `normalizeRemoveFromWorkflowAction` now exported
|
|
31
|
+
Was private in v3.8.0. Made public so tests can exercise it directly. Still not exposed via MCP — it's a runtime detail used by `getWorkflow` (read) and `buildActionChain` (write).
|
|
32
|
+
|
|
33
|
+
### Files changed
|
|
34
|
+
- `src/retry.test.ts` (NEW)
|
|
35
|
+
- `src/trigger-schemas.test.ts` (NEW)
|
|
36
|
+
- `src/workflow-builder-client.test.ts` (NEW)
|
|
37
|
+
- `src/version-check.test.ts` (NEW)
|
|
38
|
+
- `src/workflow-builder-client.ts` — `normalizeRemoveFromWorkflowAction` now exported
|
|
39
|
+
- `package.json` — `vitest` dev dep + `test` / `test:watch` scripts
|
|
40
|
+
- `.github/workflows/publish.yml` — added pre-publish test step
|
|
41
|
+
|
|
42
|
+
## 3.8.0 — Bundle re-extraction wins (triggers + goal events) + correctness fixes
|
|
43
|
+
|
|
44
|
+
**178 tools across 38 modules. Bundle: 291.3 KB.**
|
|
45
|
+
|
|
46
|
+
Three improvements that all flow from one bundle dive into `client-app-automation-workflows.leadconnectorhq.com`:
|
|
47
|
+
|
|
48
|
+
### Goal-event catalogue — all 10 conditions documented (was 1)
|
|
49
|
+
v3.6.0 shipped `build_goal_event` with one verified `goal_condition` (`review_request_clicked`) and a permissive string for everything else. The bundle's `GoalCondition` enum reveals the full set:
|
|
50
|
+
|
|
51
|
+
- `email_event` — extras: `{ stepIds: [] }`
|
|
52
|
+
- `link_click` — extras: `{ linkIds: [] }`
|
|
53
|
+
- `add_contact_tag` — extras: `{ tags: [] }` (inferred)
|
|
54
|
+
- `remove_contact_tag` — extras: `{ tags: [] }` (inferred)
|
|
55
|
+
- `appointment_status` — extras: `{ calendarId }`
|
|
56
|
+
- `payment_received` — extras: `{ globalProductIds: [] }`
|
|
57
|
+
- `form_submission` — extras: `{ formIds: [] }`
|
|
58
|
+
- `document_status` — extras: `{ templateId }`
|
|
59
|
+
- `invoice_paid` — extras: `{ invoiceStepId }`
|
|
60
|
+
- `review_request_clicked` — extras: `{ reviewTypes, reviewLinkId }`
|
|
61
|
+
|
|
62
|
+
`build_goal_event`'s `goal_condition` is now a `z.enum` of these 10 values (was permissive `z.string()`). The per-condition extras shapes are documented in the tool description.
|
|
63
|
+
|
|
64
|
+
### Goal-event action enum fixed
|
|
65
|
+
v3.6.0's tool advertised `action: "exit" | "continue" | "goto"`. The actual `GoalAction` enum in the bundle is `continue | wait | exit` — **no `goto`**. Fixed. Also removed the `target_node_id` parameter that paired with the bogus goto. `WorkflowGoalAttributes` type updated to match.
|
|
66
|
+
|
|
67
|
+
### 7 of 9 previously-uncaptured triggers now documented
|
|
68
|
+
Trigger field paths extracted from per-trigger validator functions in the bundle:
|
|
69
|
+
|
|
70
|
+
- `affiliate_created` → `affiliate.id, contact.tags`
|
|
71
|
+
- `scheduler_trigger` → `scheduler.interval, scheduler.cron, scheduler.frequency`
|
|
72
|
+
- `user_log_in` → `product.id, category.id, lesson.id, offer.id, contact.tags` (shares membership-course validator)
|
|
73
|
+
- `order_submission` → `order.funnel_id, order.line_item_global_product_ids, order.line_item_funnel_product_ids, payment.calendar.id, payment.global_product_ids, payment.form.id`
|
|
74
|
+
- `facebook_lead_gen` → `facebook.formId, facebook.pageId, contact.tags`
|
|
75
|
+
|
|
76
|
+
Plus 2 more handled explicitly:
|
|
77
|
+
- `conv_ai_trigger`, `conv_ai_autonomous_trigger` — confirmed fieldless (no specific validator in the bundle)
|
|
78
|
+
- `custom_object_created`, `custom_object_changed` — fields are user-defined per object using the `customObject.<field>` prefix; schema now documents this dynamic shape
|
|
79
|
+
|
|
80
|
+
Coverage: **57/57 native triggers, 0 type-only-pending** (was 9 type-only-pending).
|
|
81
|
+
|
|
82
|
+
### Bug fix: `remove_from_workflow` read/write asymmetry
|
|
83
|
+
Don Harris flagged at the end of his 2026-05-14 funnel audit that some GHL workflows return only the `workflow_id` array on read, while writes require both that AND the `workflowId` string. Couldn't reproduce in MCP Testing — both reads I checked returned both fields — but added defensive symmetric normalization to both paths in `workflow-builder-client.ts`. If only one form is present in either direction, the other is synthesized. No-op when both are already present.
|
|
84
|
+
|
|
85
|
+
### Bug fix: accurate "without Firebase" tool count
|
|
86
|
+
Empirical audit: basic-creds mode (no Firebase) actually exposes **148 tools**, not 168 as the v3.5.1 messaging claimed. Firebase adds **30 tools** across 6 modules (workflow builder, funnel builder, form builder, pipeline builder, workflow cloner, validate_workflow). `setup_ghl_mcp`, `enable_workflow_builder`, README, and CLAUDE.md all corrected.
|
|
87
|
+
|
|
88
|
+
### Bundle re-extraction methodology (for future audits)
|
|
89
|
+
1. Fetch `https://client-app-automation-workflows.leadconnectorhq.com/` → find current `index-*.js` bundle URL
|
|
90
|
+
2. Pull the bundle (~9.5 MB minified)
|
|
91
|
+
3. Search for `var GoalCondition=(...)` and `var GoalAction=(...)` for enum extraction
|
|
92
|
+
4. Search for `<triggerName>Validator=n=>` patterns for per-trigger field paths
|
|
93
|
+
5. Watch for i18n translation pollution — filter out windows containing non-ASCII Danish/German/Swedish characters
|
|
94
|
+
|
|
95
|
+
### Files changed
|
|
96
|
+
- `src/trigger-schemas.ts` — 7 uncaptured triggers now documented; 2 confirmed fieldless; 2 use dynamic `customObject.*` schema
|
|
97
|
+
- `src/tools/workflow-builder.ts` — `build_goal_event` enum tightened; goto removed; description lists all 10 goal_condition values + extras shapes
|
|
98
|
+
- `src/workflow-action-types.ts` — `WorkflowGoalAttributes.action` corrected to `continue | wait | exit`
|
|
99
|
+
- `src/workflow-builder-client.ts` — `normalizeRemoveFromWorkflowAction()` helper applied symmetrically on read + write
|
|
100
|
+
- `src/setup-tool.ts` — 168 → 148; "9 workflow builder tools" → "30 tools across 6 modules"
|
|
101
|
+
- `README.md`, `CLAUDE.md` — synced
|
|
102
|
+
|
|
3
103
|
## 3.7.0 — Funnel-builder fixes (Don Harris audit)
|
|
4
104
|
|
|
5
105
|
**178 tools across 38 modules. Bundle: 288.4 KB.**
|
package/README.md
CHANGED
|
@@ -138,7 +138,7 @@ https://app.gohighlevel.com/v2/location/YOUR_LOCATION_ID/dashboard
|
|
|
138
138
|
|
|
139
139
|
## Enable Workflow Builder (Optional)
|
|
140
140
|
|
|
141
|
-
The
|
|
141
|
+
The 30 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 148 tools work fine — you just won'''t have workflow/funnel/form/pipeline editing.
|
|
142
142
|
|
|
143
143
|
Grab the three values from your GHL browser session, then re-run `setup_ghl_mcp` with them:
|
|
144
144
|
|
|
@@ -420,7 +420,7 @@ Re-run setup_ghl_mcp with workflow builder:
|
|
|
420
420
|
|---|---|
|
|
421
421
|
| `get_mcp_version` | Check installed version against the latest published to npm. Confirms an upgrade landed after restarting Claude. Available even before GHL credentials are configured. |
|
|
422
422
|
| `health_check` | Run a full health check: npm registry + version status, GHL API key validity, default location reachability, Firebase auth status, token registry presence. Use when something feels broken. |
|
|
423
|
-
| `enable_workflow_builder` | Add Firebase credentials to an existing install to unlock
|
|
423
|
+
| `enable_workflow_builder` | Add Firebase credentials to an existing install to unlock 30 additional tools across 6 modules (workflow builder, funnel builder, form builder, pipeline builder, workflow cloner, validate_workflow). No need to re-enter license / API key / location ID. Run this any time after the basic setup, on the buyer's schedule. |
|
|
424
424
|
|
|
425
425
|
### Other Modules
|
|
426
426
|
|
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.
|
|
34
|
+
version: "3.8.1",
|
|
35
35
|
description: "GoHighLevel MCP Server for Claude. 178 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
|
|
36
36
|
main: "dist/index.js",
|
|
37
37
|
bin: {
|
|
@@ -50,6 +50,8 @@ var require_package = __commonJS({
|
|
|
50
50
|
setup: "node setup-wizard.mjs",
|
|
51
51
|
start: "node dist/index.js",
|
|
52
52
|
dev: "tsc --watch",
|
|
53
|
+
test: "vitest run",
|
|
54
|
+
"test:watch": "vitest",
|
|
53
55
|
prepublishOnly: "npm run build"
|
|
54
56
|
},
|
|
55
57
|
keywords: [
|
|
@@ -87,7 +89,8 @@ var require_package = __commonJS({
|
|
|
87
89
|
"@types/node": "^22.15.3",
|
|
88
90
|
esbuild: "^0.27.4",
|
|
89
91
|
open: "^11.0.0",
|
|
90
|
-
typescript: "^5.8.3"
|
|
92
|
+
typescript: "^5.8.3",
|
|
93
|
+
vitest: "^4.1.6"
|
|
91
94
|
}
|
|
92
95
|
};
|
|
93
96
|
}
|
|
@@ -651,15 +654,60 @@ var FacebookCommentOnPostTriggerSchema = typedTrigger("facebook_comment_on_post"
|
|
|
651
654
|
var IgCommentOnPostTriggerSchema = typedTrigger("ig_comment_on_post", [], "partial");
|
|
652
655
|
var InboundWebhookTriggerSchema = typedTrigger("inbound_webhook", [], "fieldless");
|
|
653
656
|
var PaymentReceivedTriggerSchema = typedTrigger("payment_received", [], "fieldless");
|
|
654
|
-
var AffiliateCreatedTriggerSchema = typedTrigger("affiliate_created", [
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
var
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
657
|
+
var AffiliateCreatedTriggerSchema = typedTrigger("affiliate_created", [
|
|
658
|
+
"affiliate.id",
|
|
659
|
+
"contact.tags"
|
|
660
|
+
]);
|
|
661
|
+
var SchedulerTriggerTriggerSchema = typedTrigger("scheduler_trigger", [
|
|
662
|
+
"scheduler.interval",
|
|
663
|
+
"scheduler.cron",
|
|
664
|
+
"scheduler.frequency"
|
|
665
|
+
]);
|
|
666
|
+
var UserLogInTriggerSchema = typedTrigger("user_log_in", [
|
|
667
|
+
// Shares membershipCourseValidator with course/lesson/category triggers.
|
|
668
|
+
"product.id",
|
|
669
|
+
"category.id",
|
|
670
|
+
"lesson.id",
|
|
671
|
+
"offer.id",
|
|
672
|
+
"contact.tags"
|
|
673
|
+
]);
|
|
674
|
+
var OrderSubmissionTriggerSchema = typedTrigger("order_submission", [
|
|
675
|
+
"order.funnel_id",
|
|
676
|
+
"order.line_item_global_product_ids",
|
|
677
|
+
"order.line_item_funnel_product_ids",
|
|
678
|
+
"payment.calendar.id",
|
|
679
|
+
"payment.global_product_ids",
|
|
680
|
+
"payment.form.id"
|
|
681
|
+
]);
|
|
682
|
+
var FacebookLeadGenTriggerSchema = typedTrigger("facebook_lead_gen", [
|
|
683
|
+
"facebook.formId",
|
|
684
|
+
"facebook.pageId",
|
|
685
|
+
"contact.tags"
|
|
686
|
+
]);
|
|
687
|
+
var CustomObjectCreatedTriggerSchema = TriggerCommonSchema.extend({
|
|
688
|
+
type: import_zod2.z.literal("custom_object_created"),
|
|
689
|
+
conditions: import_zod2.z.array(import_zod2.z.object({
|
|
690
|
+
operator: import_zod2.z.string(),
|
|
691
|
+
field: import_zod2.z.string().describe("Custom-object field paths use the `customObject.<your_field_name>` prefix. Field names are user-defined per custom object \u2014 query the location's custom objects to discover valid field paths."),
|
|
692
|
+
value: import_zod2.z.unknown().optional(),
|
|
693
|
+
title: import_zod2.z.string().optional(),
|
|
694
|
+
type: import_zod2.z.string().optional(),
|
|
695
|
+
id: import_zod2.z.string().optional()
|
|
696
|
+
}).passthrough()).optional()
|
|
697
|
+
});
|
|
698
|
+
var CustomObjectChangedTriggerSchema = TriggerCommonSchema.extend({
|
|
699
|
+
type: import_zod2.z.literal("custom_object_changed"),
|
|
700
|
+
conditions: import_zod2.z.array(import_zod2.z.object({
|
|
701
|
+
operator: import_zod2.z.string(),
|
|
702
|
+
field: import_zod2.z.string().describe("Custom-object field paths use the `customObject.<your_field_name>` prefix. Field names are user-defined per custom object \u2014 query the location's custom objects to discover valid field paths."),
|
|
703
|
+
value: import_zod2.z.unknown().optional(),
|
|
704
|
+
title: import_zod2.z.string().optional(),
|
|
705
|
+
type: import_zod2.z.string().optional(),
|
|
706
|
+
id: import_zod2.z.string().optional()
|
|
707
|
+
}).passthrough()).optional()
|
|
708
|
+
});
|
|
709
|
+
var ConvAiTriggerTriggerSchema = typedTrigger("conv_ai_trigger", [], "fieldless");
|
|
710
|
+
var ConvAiAutonomousTriggerTriggerSchema = typedTrigger("conv_ai_autonomous_trigger", [], "fieldless");
|
|
663
711
|
var UnknownTriggerSchema = TriggerCommonSchema.extend({
|
|
664
712
|
type: import_zod2.z.string(),
|
|
665
713
|
conditions: import_zod2.z.array(import_zod2.z.record(import_zod2.z.unknown())).optional()
|
|
@@ -770,6 +818,23 @@ var CreateWorkflowResponseSchema = import_zod3.z.union([
|
|
|
770
818
|
version: typeof data.version === "number" ? data.version : 1
|
|
771
819
|
});
|
|
772
820
|
});
|
|
821
|
+
function normalizeRemoveFromWorkflowAction(action) {
|
|
822
|
+
if (action.type !== "remove_from_workflow") return action;
|
|
823
|
+
const attrs = action.attributes;
|
|
824
|
+
if (!attrs || typeof attrs !== "object") return action;
|
|
825
|
+
const a = attrs;
|
|
826
|
+
const hasArr = Array.isArray(a.workflow_id) && a.workflow_id.length > 0;
|
|
827
|
+
const hasStr = typeof a.workflowId === "string" && a.workflowId.length > 0;
|
|
828
|
+
if (hasArr && hasStr) return action;
|
|
829
|
+
if (!hasArr && !hasStr) return action;
|
|
830
|
+
const next = { ...a };
|
|
831
|
+
if (hasArr && !hasStr) {
|
|
832
|
+
next.workflowId = a.workflow_id[0];
|
|
833
|
+
} else if (hasStr && !hasArr) {
|
|
834
|
+
next.workflow_id = [a.workflowId];
|
|
835
|
+
}
|
|
836
|
+
return { ...action, attributes: next };
|
|
837
|
+
}
|
|
773
838
|
function hasId(action) {
|
|
774
839
|
return typeof action.id === "string" && action.id.length > 0;
|
|
775
840
|
}
|
|
@@ -1074,7 +1139,11 @@ ${errorBody}`
|
|
|
1074
1139
|
workflowData: isRecord(raw.workflowData.workflowData) ? raw.workflowData.workflowData : { templates: [] },
|
|
1075
1140
|
triggers: raw.triggers
|
|
1076
1141
|
} : raw;
|
|
1077
|
-
|
|
1142
|
+
const parsed = WorkflowFullSchema.parse(flat);
|
|
1143
|
+
if (parsed.workflowData?.templates) {
|
|
1144
|
+
parsed.workflowData.templates = parsed.workflowData.templates.map(normalizeRemoveFromWorkflowAction);
|
|
1145
|
+
}
|
|
1146
|
+
return parsed;
|
|
1078
1147
|
}
|
|
1079
1148
|
/**
|
|
1080
1149
|
* Create a new empty workflow
|
|
@@ -1235,7 +1304,7 @@ ${errorBody}`
|
|
|
1235
1304
|
*/
|
|
1236
1305
|
buildActionChain(actions) {
|
|
1237
1306
|
validateActionChain(actions);
|
|
1238
|
-
const linked = actions.map((action, i) => {
|
|
1307
|
+
const linked = actions.map(normalizeRemoveFromWorkflowAction).map((action, i) => {
|
|
1239
1308
|
const copy = { ...action };
|
|
1240
1309
|
if (!copy.id) {
|
|
1241
1310
|
copy.id = crypto.randomUUID();
|
|
@@ -4308,21 +4377,28 @@ function registerWorkflowBuilderTools(server2, client) {
|
|
|
4308
4377
|
);
|
|
4309
4378
|
server2.tool(
|
|
4310
4379
|
"build_goal_event",
|
|
4311
|
-
"Build a correctly-shaped GHL workflow goal-event node. Goal events sit inline in the action chain \u2014 when the goal condition fires during workflow execution, the configured action runs (default: exit the workflow). The previous action's `next` should point to the goal node's id; the goal node itself does not have a `next`.
|
|
4312
|
-
{
|
|
4313
|
-
goal_condition: import_zod31.z.
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4380
|
+
"Build a correctly-shaped GHL workflow goal-event node. Goal events sit inline in the action chain \u2014 when the goal condition fires during workflow execution, the configured action runs (default: exit the workflow). The previous action's `next` should point to the goal node's id; the goal node itself does not have a `next`. All 10 goal_condition values are catalogued (from GHL's workflow-builder JS bundle, extracted 2026-05-18): email_event, link_click, add_contact_tag, remove_contact_tag, appointment_status, payment_received, form_submission, document_status, invoice_paid, review_request_clicked. Each one has a different `extras` shape (see the `goal_condition` field's description for details).",
|
|
4381
|
+
{
|
|
4382
|
+
goal_condition: import_zod31.z.enum([
|
|
4383
|
+
"email_event",
|
|
4384
|
+
"link_click",
|
|
4385
|
+
"add_contact_tag",
|
|
4386
|
+
"remove_contact_tag",
|
|
4387
|
+
"appointment_status",
|
|
4388
|
+
"payment_received",
|
|
4389
|
+
"form_submission",
|
|
4390
|
+
"document_status",
|
|
4391
|
+
"invoice_paid",
|
|
4392
|
+
"review_request_clicked"
|
|
4393
|
+
]).describe("The goal-condition identifier. Extras shape per condition: email_event \u2192 {stepIds:[]}; link_click \u2192 {linkIds:[]}; appointment_status \u2192 {calendarId}; payment_received \u2192 {globalProductIds:[]}; form_submission \u2192 {formIds:[]}; document_status \u2192 {templateId}; invoice_paid \u2192 {invoiceStepId}; review_request_clicked \u2192 {reviewTypes:['sms'|'email'], reviewLinkId}; add_contact_tag/remove_contact_tag \u2192 {tags:[]} (uncaptured but inferred)."),
|
|
4394
|
+
extras: import_zod31.z.record(import_zod31.z.unknown()).optional().describe("Goal-condition-specific config. See goal_condition description for the expected shape per value. Pass {} or omit for goal_conditions without extras."),
|
|
4395
|
+
action: import_zod31.z.enum(["exit", "continue", "wait"]).default("exit").describe("What happens when the goal fires (per GHL's GoalAction enum). 'exit' terminates the workflow path (verified). 'continue' lets it proceed to a downstream node. 'wait' pauses on the goal."),
|
|
4317
4396
|
name: import_zod31.z.string().default("Goal").describe("Display name in the GHL UI."),
|
|
4318
4397
|
op: import_zod31.z.enum(["or", "and"]).default("or").describe("Top-level boolean operator across segments. Default 'or'."),
|
|
4319
4398
|
inner_op: import_zod31.z.enum(["or", "and"]).default("or").describe("Boolean operator across conditions within a single segment. Default 'or'.")
|
|
4320
4399
|
},
|
|
4321
|
-
async ({ goal_condition, extras, action,
|
|
4400
|
+
async ({ goal_condition, extras, action, name, op, inner_op }) => {
|
|
4322
4401
|
try {
|
|
4323
|
-
if (action === "goto" && !target_node_id) {
|
|
4324
|
-
throw new Error("action='goto' requires target_node_id to identify which workflow action to jump to.");
|
|
4325
|
-
}
|
|
4326
4402
|
const nodeId = crypto.randomUUID();
|
|
4327
4403
|
const conditionId = crypto.randomUUID();
|
|
4328
4404
|
const attributes = {
|
|
@@ -4338,9 +4414,6 @@ function registerWorkflowBuilderTools(server2, client) {
|
|
|
4338
4414
|
type: "workflow_goal",
|
|
4339
4415
|
action
|
|
4340
4416
|
};
|
|
4341
|
-
if (action === "goto" && target_node_id) {
|
|
4342
|
-
attributes.targetNodeId = target_node_id;
|
|
4343
|
-
}
|
|
4344
4417
|
const goalNode = {
|
|
4345
4418
|
id: nodeId,
|
|
4346
4419
|
name,
|
|
@@ -6724,7 +6797,7 @@ async function validateFirebase(firebaseKey, refreshToken) {
|
|
|
6724
6797
|
function registerSetupTool(server2) {
|
|
6725
6798
|
server2.tool(
|
|
6726
6799
|
"setup_ghl_mcp",
|
|
6727
|
-
"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 178 tools (
|
|
6800
|
+
"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 178 tools (148 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
|
|
6728
6801
|
{
|
|
6729
6802
|
email: import_zod42.z.string().email().describe("Email used at purchase."),
|
|
6730
6803
|
license_key: import_zod42.z.string().min(20).describe("License key from your purchase email."),
|
|
@@ -6779,7 +6852,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
|
|
|
6779
6852
|
ghl_firebase_api_key: workflowBuilderEnabled ? args.ghl_firebase_api_key?.trim() : void 0,
|
|
6780
6853
|
ghl_firebase_refresh_token: workflowBuilderEnabled ? args.ghl_firebase_refresh_token?.trim() : void 0
|
|
6781
6854
|
});
|
|
6782
|
-
const toolCount = workflowBuilderEnabled ? "178" : "
|
|
6855
|
+
const toolCount = workflowBuilderEnabled ? "178" : "148";
|
|
6783
6856
|
const wfLine = workflowBuilderEnabled ? "Workflow Builder: enabled." : "Workflow Builder: not configured (optional).";
|
|
6784
6857
|
const wfTip = workflowBuilderEnabled ? "" : "\nTo enable Workflow Builder later (8 extra tools): run enable_workflow_builder with your three Firebase values. No need to re-enter license/API key/location ID.";
|
|
6785
6858
|
return {
|
|
@@ -6807,7 +6880,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
|
|
|
6807
6880
|
function registerEnableWorkflowBuilderTool(server2) {
|
|
6808
6881
|
server2.tool(
|
|
6809
6882
|
"enable_workflow_builder",
|
|
6810
|
-
"Add Firebase credentials to an existing GHL Command install to unlock
|
|
6883
|
+
"Add Firebase credentials to an existing GHL Command install to unlock 30 additional tools across 6 modules: workflow builder (create/edit/clone/delete/publish/validate workflows, build_if_else_branch, build_goal_event, get_trigger_registry), funnel + page builder (10 tools), form builder (5 tools), pipeline builder (5 tools), and workflow cloning. Requires you've already run setup_ghl_mcp. Capture the three Firebase values from your GHL browser session \u2014 see elitedcs.com/ghl-mcp-firebase for step-by-step DevTools instructions. Tool count goes from 148 to 178 after the next Claude restart.",
|
|
6811
6884
|
{
|
|
6812
6885
|
ghl_user_id: import_zod42.z.string().min(10).describe("Firebase User ID (uid). DevTools \u2192 Application \u2192 IndexedDB \u2192 firebaseLocalStorageDb \u2192 firebaseLocalStorage \u2192 the value.uid field of the firebase:authUser row."),
|
|
6813
6886
|
ghl_firebase_api_key: import_zod42.z.string().min(10).describe("Firebase API Key starting with 'AIza'. The string between 'firebase:authUser:' and ':[DEFAULT]' in the row's Key column."),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"description": "GoHighLevel MCP Server for Claude. 178 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"setup": "node setup-wizard.mjs",
|
|
20
20
|
"start": "node dist/index.js",
|
|
21
21
|
"dev": "tsc --watch",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
22
24
|
"prepublishOnly": "npm run build"
|
|
23
25
|
},
|
|
24
26
|
"keywords": [
|
|
@@ -56,6 +58,7 @@
|
|
|
56
58
|
"@types/node": "^22.15.3",
|
|
57
59
|
"esbuild": "^0.27.4",
|
|
58
60
|
"open": "^11.0.0",
|
|
59
|
-
"typescript": "^5.8.3"
|
|
61
|
+
"typescript": "^5.8.3",
|
|
62
|
+
"vitest": "^4.1.6"
|
|
60
63
|
}
|
|
61
64
|
}
|