@elitedcs/ghl-mcp 3.10.1 → 3.11.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 +79 -0
- package/README.md +1 -1
- package/dist/index.js +243 -62
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.11.1 — Reputation: review-link list (partial gap-closure round 4)
|
|
4
|
+
|
|
5
|
+
**197 tools across 41 modules. Bundle: ~315 KB.**
|
|
6
|
+
|
|
7
|
+
Partial close on the reputation gap. One verified tool shipped; the bigger reviews surface needs a DevTools capture against a live account.
|
|
8
|
+
|
|
9
|
+
### New tool (Firebase-gated)
|
|
10
|
+
|
|
11
|
+
- **`get_review_link_list`** — lists the review-link destinations (Google, Facebook, etc.) configured for a location. Each entry has a label + the public review URL. Useful for building review-request workflows: the workflow goal condition `review_request_clicked` (shipped in v3.8.0) references these review-link ids.
|
|
12
|
+
|
|
13
|
+
Endpoint: `GET backend.leadconnectorhq.com/reputation/integrations/review-link-list?locationId=X` via Firebase auth. Verified 2026-05-22.
|
|
14
|
+
|
|
15
|
+
### What's NOT shipped + why
|
|
16
|
+
|
|
17
|
+
The core reputation surface — listing the reviews that come BACK from Google/Facebook, responding to them, review-request campaigns — sits behind `/reputation/reviews`. That endpoint returns `401 "Unauthorized Access: undefined - No Location Found"` regardless of:
|
|
18
|
+
- bearer (PIT) vs Firebase auth
|
|
19
|
+
- which location header/param is sent (`location-id`, `locationid`, `location`, query param, path segment)
|
|
20
|
+
|
|
21
|
+
The `undefined` in the error means the endpoint reads a location field from the request that comes back empty — the GHL reputation UI sends something specific that black-box probing can't reproduce. **This needs a DevTools capture** of the exact request the reputation UI makes, against a live account that has connected review platforms (MCP Testing has none, so there'd be nothing to verify against anyway).
|
|
22
|
+
|
|
23
|
+
Documented as capture-pending in the tool's own description so the LLM gives buyers an honest answer.
|
|
24
|
+
|
|
25
|
+
### Tool count impact
|
|
26
|
+
|
|
27
|
+
- Total: 196 → 197 (+1)
|
|
28
|
+
- Firebase-gated (the new tool uses internal-API auth)
|
|
29
|
+
- Modules: 40 → 41 (new reputation module)
|
|
30
|
+
|
|
31
|
+
### Files changed
|
|
32
|
+
|
|
33
|
+
- `src/tools/reputation.ts` — NEW
|
|
34
|
+
- `src/tools/index.ts` — registered `registerReputationTools`
|
|
35
|
+
- `src/setup-tool.ts` — Firebase-mode count 196 → 197
|
|
36
|
+
|
|
37
|
+
## 3.11.0 — Smart Lists CRUD (gap-closure round 3)
|
|
38
|
+
|
|
39
|
+
**196 tools across 40 modules. Bundle: 315.1 KB.**
|
|
40
|
+
|
|
41
|
+
Closes the Smart Lists gap with full CRUD. Smart Lists are saved searches over contacts or opportunities — agencies use them heavily for segmentation, campaign targeting, and workflow trigger filters.
|
|
42
|
+
|
|
43
|
+
### 5 new tools (Firebase-gated)
|
|
44
|
+
|
|
45
|
+
- **`list_smart_lists`** — paginated by `objectKey` (`contacts` or `opportunity`). Supports free-text name search.
|
|
46
|
+
- **`get_smart_list`** — single list with full filter spec, columns, permissions.
|
|
47
|
+
- **`create_smart_list`** — required: name + objectKey. Optional: filters, columns, pipelineIds, defaultInPipelines.
|
|
48
|
+
- **`update_smart_list`** — partial updates work. objectKey can't be changed after creation.
|
|
49
|
+
- **`delete_smart_list`** — confirm-gated.
|
|
50
|
+
|
|
51
|
+
### Discovery story
|
|
52
|
+
|
|
53
|
+
The workflow-builder bundle declares `smartListBackendURL` as config but doesn't actually use it. Real path was in the same bundle under `SmartListService extends BaseService`:
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
new SmartListService(`${config.marketPlaceBackendURL}/lists/dynamic/`, ...)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
That resolves to `backend.leadconnectorhq.com/lists/dynamic/{locationId}` — verified all 5 CRUD endpoints (list / get / create / update / delete) end-to-end.
|
|
60
|
+
|
|
61
|
+
### Why Firebase-gated, not public
|
|
62
|
+
|
|
63
|
+
Tried bearer-token auth on the same endpoints — returns 400 `"Cannot list smartlists"`. Smart Lists require the internal-API auth path (Firebase ID token via the `token-id` header), same gate as workflow-builder, funnel-builder, form-builder, and pipeline-builder.
|
|
64
|
+
|
|
65
|
+
### Tool count impact
|
|
66
|
+
|
|
67
|
+
- Total: 191 → 196 (+5)
|
|
68
|
+
- With Firebase: 191 → 196 (+5 unlocked when enable_workflow_builder runs)
|
|
69
|
+
- Without Firebase: 161 (unchanged — these are internal-API tools)
|
|
70
|
+
- Modules: 39 → 40 (new smart-lists module)
|
|
71
|
+
|
|
72
|
+
### Verified end-to-end against MCP Testing
|
|
73
|
+
|
|
74
|
+
Round-trip on `objectKey: "contacts"`: create → get → list (search) → update (rename) → delete. 5/5 success, no probe pollution left in the sub-account.
|
|
75
|
+
|
|
76
|
+
### Files changed
|
|
77
|
+
|
|
78
|
+
- `src/tools/smart-lists.ts` — NEW
|
|
79
|
+
- `src/tools/index.ts` — registered `registerSmartListTools` alongside other internal-API builders
|
|
80
|
+
- `src/setup-tool.ts` — bumped Firebase-mode tool count (191 → 196)
|
|
81
|
+
|
|
3
82
|
## 3.10.1 — `list_message_templates` (SMS / email-snippet / WhatsApp read)
|
|
4
83
|
|
|
5
84
|
**191 tools across 39 modules. Bundle: ~304.7 KB.**
|
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.** 197 tools across 41 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.
|
|
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
|
|
package/dist/index.js
CHANGED
|
@@ -31,8 +31,8 @@ var require_package = __commonJS({
|
|
|
31
31
|
"package.json"(exports2, module2) {
|
|
32
32
|
module2.exports = {
|
|
33
33
|
name: "@elitedcs/ghl-mcp",
|
|
34
|
-
version: "3.
|
|
35
|
-
description: "GoHighLevel MCP Server for Claude.
|
|
34
|
+
version: "3.11.1",
|
|
35
|
+
description: "GoHighLevel MCP Server for Claude. 197 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
|
|
36
36
|
main: "dist/index.js",
|
|
37
37
|
bin: {
|
|
38
38
|
"ghl-mcp": "dist/index.js"
|
|
@@ -6010,42 +6010,221 @@ function registerWorkflowClonerTools(server2, builderClient) {
|
|
|
6010
6010
|
);
|
|
6011
6011
|
}
|
|
6012
6012
|
|
|
6013
|
-
// src/tools/
|
|
6013
|
+
// src/tools/smart-lists.ts
|
|
6014
6014
|
var import_zod40 = require("zod");
|
|
6015
|
+
var SMARTLIST_BASE = "https://backend.leadconnectorhq.com/lists/dynamic";
|
|
6016
|
+
var OBJECT_KEYS = ["contacts", "opportunity"];
|
|
6017
|
+
function registerSmartListTools(server2, builderClient) {
|
|
6018
|
+
const client = builderClient;
|
|
6019
|
+
if (!client) return;
|
|
6020
|
+
async function smartListRequest(method, path6, body) {
|
|
6021
|
+
const headers = await client.buildHeaders();
|
|
6022
|
+
const url = `${SMARTLIST_BASE}${path6}`;
|
|
6023
|
+
const options = { method, headers };
|
|
6024
|
+
if (body && (method === "POST" || method === "PUT")) {
|
|
6025
|
+
options.body = JSON.stringify(body);
|
|
6026
|
+
}
|
|
6027
|
+
const response = await fetch(url, options);
|
|
6028
|
+
if (!response.ok) {
|
|
6029
|
+
const text2 = await response.text();
|
|
6030
|
+
throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path6}
|
|
6031
|
+
${text2}`);
|
|
6032
|
+
}
|
|
6033
|
+
const text = await response.text();
|
|
6034
|
+
if (!text) return {};
|
|
6035
|
+
return JSON.parse(text);
|
|
6036
|
+
}
|
|
6037
|
+
server2.tool(
|
|
6038
|
+
"list_smart_lists",
|
|
6039
|
+
"List smart lists (dynamic / saved-filter lists) in a location. Smart Lists are saved searches over contacts or opportunities \u2014 agencies use them to segment by complex criteria. Filters and columns aren't returned in the list view; use get_smart_list for the full filter spec.",
|
|
6040
|
+
{
|
|
6041
|
+
objectKey: import_zod40.z.enum(OBJECT_KEYS).describe("The object type the lists segment over. 'contacts' for contact-segments, 'opportunity' for opportunity-segments. Required \u2014 GHL rejects requests without it."),
|
|
6042
|
+
query: import_zod40.z.string().optional().describe("Free-text search across smart list names."),
|
|
6043
|
+
limit: import_zod40.z.number().optional().describe("Max smart lists per page. Defaults to 20 on GHL's side."),
|
|
6044
|
+
startAfter: import_zod40.z.string().optional().describe("Cursor for pagination \u2014 pass the last list's id from the previous page."),
|
|
6045
|
+
locationId: import_zod40.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
6046
|
+
},
|
|
6047
|
+
async ({ objectKey, query, limit, startAfter, locationId: locationId2 }) => {
|
|
6048
|
+
try {
|
|
6049
|
+
const loc = locationId2 ?? client.locationId;
|
|
6050
|
+
const params = new URLSearchParams({ objectKey });
|
|
6051
|
+
if (query) params.set("query", query);
|
|
6052
|
+
if (limit !== void 0) params.set("limit", String(limit));
|
|
6053
|
+
if (startAfter) params.set("startAfter", startAfter);
|
|
6054
|
+
const result = await smartListRequest("GET", `/${loc}?${params.toString()}`);
|
|
6055
|
+
return jsonResponse(result);
|
|
6056
|
+
} catch (error) {
|
|
6057
|
+
return errorResponse(error);
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
6060
|
+
);
|
|
6061
|
+
server2.tool(
|
|
6062
|
+
"get_smart_list",
|
|
6063
|
+
"Get a single smart list by ID with its full configuration: filters, columns, permissions, and metadata. The filters array is what defines who/what is in the list.",
|
|
6064
|
+
{
|
|
6065
|
+
listId: import_zod40.z.string().describe("The smart list ID (from list_smart_lists or a previous create_smart_list response)."),
|
|
6066
|
+
locationId: import_zod40.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
6067
|
+
},
|
|
6068
|
+
async ({ listId, locationId: locationId2 }) => {
|
|
6069
|
+
try {
|
|
6070
|
+
const loc = locationId2 ?? client.locationId;
|
|
6071
|
+
const result = await smartListRequest("GET", `/${loc}/${listId}`);
|
|
6072
|
+
return jsonResponse(result);
|
|
6073
|
+
} catch (error) {
|
|
6074
|
+
return errorResponse(error);
|
|
6075
|
+
}
|
|
6076
|
+
}
|
|
6077
|
+
);
|
|
6078
|
+
server2.tool(
|
|
6079
|
+
"create_smart_list",
|
|
6080
|
+
"Create a new smart list (dynamic filter list). Required: name + objectKey. Filters define the saved-search criteria \u2014 pass an empty array to create an empty list and add filters later via update_smart_list. The shape of filters/columns is opaque here; query an existing smart list with get_smart_list to see the format GHL expects.",
|
|
6081
|
+
{
|
|
6082
|
+
name: import_zod40.z.string().describe("Display name for the smart list."),
|
|
6083
|
+
objectKey: import_zod40.z.enum(OBJECT_KEYS).describe("Object type the list segments over. 'contacts' or 'opportunity'."),
|
|
6084
|
+
filters: import_zod40.z.array(import_zod40.z.record(import_zod40.z.unknown())).optional().describe("Array of filter objects. Each object has fields like {field, operator, value} \u2014 exact shape varies by filter type. See get_smart_list on an existing list to learn the format."),
|
|
6085
|
+
columns: import_zod40.z.array(import_zod40.z.record(import_zod40.z.unknown())).optional().describe("Array of column definitions for the smart list view in GHL UI. Each defines which contact/opportunity field shows as a column. Defaults to GHL's standard columns if omitted."),
|
|
6086
|
+
pipelineIds: import_zod40.z.array(import_zod40.z.string()).optional().describe("(opportunity objectKey only) Pipeline IDs to restrict this smart list to. Empty array = all pipelines."),
|
|
6087
|
+
defaultInPipelines: import_zod40.z.array(import_zod40.z.string()).optional().describe("(opportunity objectKey only) Pipeline IDs where this list is the default view."),
|
|
6088
|
+
locationId: import_zod40.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
6089
|
+
},
|
|
6090
|
+
async ({ name, objectKey, filters, columns, pipelineIds, defaultInPipelines, locationId: locationId2 }) => {
|
|
6091
|
+
try {
|
|
6092
|
+
const loc = locationId2 ?? client.locationId;
|
|
6093
|
+
const body = { name, objectKey };
|
|
6094
|
+
if (filters !== void 0) body.filters = filters;
|
|
6095
|
+
if (columns !== void 0) body.columns = columns;
|
|
6096
|
+
if (pipelineIds !== void 0) body.pipelineIds = pipelineIds;
|
|
6097
|
+
if (defaultInPipelines !== void 0) body.defaultInPipelines = defaultInPipelines;
|
|
6098
|
+
const result = await smartListRequest("POST", `/${loc}`, body);
|
|
6099
|
+
return jsonResponse(result);
|
|
6100
|
+
} catch (error) {
|
|
6101
|
+
return errorResponse(error);
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
);
|
|
6105
|
+
server2.tool(
|
|
6106
|
+
"update_smart_list",
|
|
6107
|
+
"Update an existing smart list's name, filters, or columns. The objectKey CANNOT be changed after creation (GHL rejects with 422 if you try). Use get_smart_list first to inspect the current filters; partial updates work \u2014 pass only the fields you want to change.",
|
|
6108
|
+
{
|
|
6109
|
+
listId: import_zod40.z.string().describe("The smart list ID to update."),
|
|
6110
|
+
name: import_zod40.z.string().optional().describe("New display name."),
|
|
6111
|
+
filters: import_zod40.z.array(import_zod40.z.record(import_zod40.z.unknown())).optional().describe("Replace the filter array entirely. To add a filter, fetch the current list, append, and pass the new array."),
|
|
6112
|
+
columns: import_zod40.z.array(import_zod40.z.record(import_zod40.z.unknown())).optional().describe("Replace the column array entirely."),
|
|
6113
|
+
pipelineIds: import_zod40.z.array(import_zod40.z.string()).optional().describe("(opportunity only) Update the pipeline scope."),
|
|
6114
|
+
defaultInPipelines: import_zod40.z.array(import_zod40.z.string()).optional().describe("(opportunity only) Update the default-in-pipelines list."),
|
|
6115
|
+
locationId: import_zod40.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
6116
|
+
},
|
|
6117
|
+
async ({ listId, name, filters, columns, pipelineIds, defaultInPipelines, locationId: locationId2 }) => {
|
|
6118
|
+
try {
|
|
6119
|
+
const loc = locationId2 ?? client.locationId;
|
|
6120
|
+
const body = {};
|
|
6121
|
+
if (name !== void 0) body.name = name;
|
|
6122
|
+
if (filters !== void 0) body.filters = filters;
|
|
6123
|
+
if (columns !== void 0) body.columns = columns;
|
|
6124
|
+
if (pipelineIds !== void 0) body.pipelineIds = pipelineIds;
|
|
6125
|
+
if (defaultInPipelines !== void 0) body.defaultInPipelines = defaultInPipelines;
|
|
6126
|
+
if (Object.keys(body).length === 0) {
|
|
6127
|
+
throw new Error("Pass at least one of: name, filters, columns, pipelineIds, defaultInPipelines. Nothing to update.");
|
|
6128
|
+
}
|
|
6129
|
+
const result = await smartListRequest("PUT", `/${loc}/${listId}`, body);
|
|
6130
|
+
return jsonResponse(result);
|
|
6131
|
+
} catch (error) {
|
|
6132
|
+
return errorResponse(error);
|
|
6133
|
+
}
|
|
6134
|
+
}
|
|
6135
|
+
);
|
|
6136
|
+
server2.tool(
|
|
6137
|
+
"delete_smart_list",
|
|
6138
|
+
"Permanently delete a smart list. IRREVERSIBLE. The list configuration is removed but the contacts/opportunities themselves are NOT touched \u2014 smart lists are just saved filters. Any workflow trigger / dashboard / report that referenced this list by ID will stop working.",
|
|
6139
|
+
{
|
|
6140
|
+
listId: import_zod40.z.string().describe("The smart list ID to delete."),
|
|
6141
|
+
confirm: import_zod40.z.literal("DELETE").describe("Must pass 'DELETE' to confirm this destructive action."),
|
|
6142
|
+
locationId: import_zod40.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
6143
|
+
},
|
|
6144
|
+
async ({ listId, locationId: locationId2 }) => {
|
|
6145
|
+
try {
|
|
6146
|
+
const loc = locationId2 ?? client.locationId;
|
|
6147
|
+
const result = await smartListRequest("DELETE", `/${loc}/${listId}`);
|
|
6148
|
+
return jsonResponse(result);
|
|
6149
|
+
} catch (error) {
|
|
6150
|
+
return errorResponse(error);
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
);
|
|
6154
|
+
}
|
|
6155
|
+
|
|
6156
|
+
// src/tools/reputation.ts
|
|
6157
|
+
var import_zod41 = require("zod");
|
|
6158
|
+
var REPUTATION_BASE = "https://backend.leadconnectorhq.com/reputation";
|
|
6159
|
+
function registerReputationTools(server2, builderClient) {
|
|
6160
|
+
const client = builderClient;
|
|
6161
|
+
if (!client) return;
|
|
6162
|
+
async function reputationRequest(method, path6) {
|
|
6163
|
+
const headers = await client.buildHeaders();
|
|
6164
|
+
const response = await fetch(`${REPUTATION_BASE}${path6}`, { method, headers });
|
|
6165
|
+
if (!response.ok) {
|
|
6166
|
+
const text2 = await response.text();
|
|
6167
|
+
throw new Error(`Reputation API Error ${response.status}: ${method} ${path6}
|
|
6168
|
+
${text2}`);
|
|
6169
|
+
}
|
|
6170
|
+
const text = await response.text();
|
|
6171
|
+
if (!text) return {};
|
|
6172
|
+
return JSON.parse(text);
|
|
6173
|
+
}
|
|
6174
|
+
server2.tool(
|
|
6175
|
+
"get_review_link_list",
|
|
6176
|
+
"List the review-link destinations configured for a location \u2014 the platforms (Google, Facebook, etc.) where review requests send contacts. Each entry has a label and the public review URL. Useful for: building review-request workflows (the workflow goal condition `review_request_clicked` references these review-link ids), and auditing which review platforms a sub-account has connected. NOTE: listing the actual reviews that come BACK (and responding to them) is not yet available \u2014 that endpoint needs a DevTools capture against a live account with connected review platforms.",
|
|
6177
|
+
{
|
|
6178
|
+
locationId: import_zod41.z.string().optional().describe("Location ID. Falls back to the active builder client's location.")
|
|
6179
|
+
},
|
|
6180
|
+
async ({ locationId: locationId2 }) => {
|
|
6181
|
+
try {
|
|
6182
|
+
const loc = locationId2 ?? client.locationId;
|
|
6183
|
+
const result = await reputationRequest("GET", `/integrations/review-link-list?locationId=${loc}`);
|
|
6184
|
+
return jsonResponse(result);
|
|
6185
|
+
} catch (error) {
|
|
6186
|
+
return errorResponse(error);
|
|
6187
|
+
}
|
|
6188
|
+
}
|
|
6189
|
+
);
|
|
6190
|
+
}
|
|
6191
|
+
|
|
6192
|
+
// src/tools/template-deployer.ts
|
|
6193
|
+
var import_zod42 = require("zod");
|
|
6015
6194
|
var fs3 = __toESM(require("fs"));
|
|
6016
6195
|
var path3 = __toESM(require("path"));
|
|
6017
6196
|
function delay3(ms) {
|
|
6018
6197
|
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
6019
6198
|
}
|
|
6020
|
-
var TemplateSchema =
|
|
6021
|
-
templateName:
|
|
6022
|
-
templateVersion:
|
|
6023
|
-
description:
|
|
6024
|
-
questionnaire:
|
|
6025
|
-
id:
|
|
6026
|
-
question:
|
|
6027
|
-
type:
|
|
6028
|
-
required:
|
|
6029
|
-
placeholder:
|
|
6199
|
+
var TemplateSchema = import_zod42.z.object({
|
|
6200
|
+
templateName: import_zod42.z.string(),
|
|
6201
|
+
templateVersion: import_zod42.z.string().optional(),
|
|
6202
|
+
description: import_zod42.z.string().optional().default(""),
|
|
6203
|
+
questionnaire: import_zod42.z.array(import_zod42.z.object({
|
|
6204
|
+
id: import_zod42.z.string(),
|
|
6205
|
+
question: import_zod42.z.string(),
|
|
6206
|
+
type: import_zod42.z.string(),
|
|
6207
|
+
required: import_zod42.z.boolean().optional(),
|
|
6208
|
+
placeholder: import_zod42.z.string().optional()
|
|
6030
6209
|
})).optional().default([]),
|
|
6031
|
-
location:
|
|
6032
|
-
tags:
|
|
6033
|
-
customFields:
|
|
6034
|
-
name:
|
|
6035
|
-
dataType:
|
|
6210
|
+
location: import_zod42.z.record(import_zod42.z.unknown()).optional(),
|
|
6211
|
+
tags: import_zod42.z.array(import_zod42.z.string()).optional(),
|
|
6212
|
+
customFields: import_zod42.z.array(import_zod42.z.object({
|
|
6213
|
+
name: import_zod42.z.string(),
|
|
6214
|
+
dataType: import_zod42.z.string()
|
|
6036
6215
|
})).optional(),
|
|
6037
|
-
pipelines:
|
|
6038
|
-
name:
|
|
6039
|
-
stages:
|
|
6216
|
+
pipelines: import_zod42.z.array(import_zod42.z.object({
|
|
6217
|
+
name: import_zod42.z.string(),
|
|
6218
|
+
stages: import_zod42.z.array(import_zod42.z.object({ position: import_zod42.z.number(), name: import_zod42.z.string() }))
|
|
6040
6219
|
})).optional(),
|
|
6041
|
-
workflows:
|
|
6042
|
-
name:
|
|
6043
|
-
condition:
|
|
6044
|
-
actions:
|
|
6220
|
+
workflows: import_zod42.z.array(import_zod42.z.object({
|
|
6221
|
+
name: import_zod42.z.string(),
|
|
6222
|
+
condition: import_zod42.z.string().optional(),
|
|
6223
|
+
actions: import_zod42.z.array(import_zod42.z.record(import_zod42.z.unknown())).optional().default([])
|
|
6045
6224
|
})).optional(),
|
|
6046
|
-
calendars:
|
|
6047
|
-
name:
|
|
6048
|
-
description:
|
|
6225
|
+
calendars: import_zod42.z.array(import_zod42.z.object({
|
|
6226
|
+
name: import_zod42.z.string(),
|
|
6227
|
+
description: import_zod42.z.string().optional()
|
|
6049
6228
|
})).optional()
|
|
6050
6229
|
});
|
|
6051
6230
|
function registerTemplateDeployerTools(server2, client) {
|
|
@@ -6116,7 +6295,7 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
6116
6295
|
"get_template_questionnaire",
|
|
6117
6296
|
"Get the questionnaire for a specific template. Returns all the questions that need to be answered before deploying. Present these to the user one at a time in a conversational style.",
|
|
6118
6297
|
{
|
|
6119
|
-
templateFile:
|
|
6298
|
+
templateFile: import_zod42.z.string().describe("Path to the template JSON file (from list_templates).")
|
|
6120
6299
|
},
|
|
6121
6300
|
async ({ templateFile }) => {
|
|
6122
6301
|
try {
|
|
@@ -6149,10 +6328,10 @@ function registerTemplateDeployerTools(server2, client) {
|
|
|
6149
6328
|
"deploy_template",
|
|
6150
6329
|
"Deploy a template to set up a GHL sub-account. Creates tags, custom fields, pipelines with stages, calendars, workflows, and forms based on the template and the user's questionnaire answers. This is the main setup automation tool.",
|
|
6151
6330
|
{
|
|
6152
|
-
templateFile:
|
|
6153
|
-
answers:
|
|
6154
|
-
locationId:
|
|
6155
|
-
dryRun:
|
|
6331
|
+
templateFile: import_zod42.z.string().describe("Path to the template JSON file."),
|
|
6332
|
+
answers: import_zod42.z.record(import_zod42.z.unknown()).describe("Questionnaire answers keyed by question ID (e.g. {business_name: 'My Clinic', business_phone: '+15551234567', ...})."),
|
|
6333
|
+
locationId: import_zod42.z.string().optional().describe("Location ID to deploy to. Uses default if not specified."),
|
|
6334
|
+
dryRun: import_zod42.z.boolean().optional().describe("If true, shows what would be created without actually creating anything. Defaults to false.")
|
|
6156
6335
|
},
|
|
6157
6336
|
async ({ templateFile, answers, locationId: locationId2, dryRun }) => {
|
|
6158
6337
|
try {
|
|
@@ -6398,7 +6577,7 @@ ${errors.join("\n")}` : "\nNo errors!",
|
|
|
6398
6577
|
}
|
|
6399
6578
|
|
|
6400
6579
|
// src/tools/validators.ts
|
|
6401
|
-
var
|
|
6580
|
+
var import_zod43 = require("zod");
|
|
6402
6581
|
function extractFromTrigger(trigger, refs) {
|
|
6403
6582
|
const triggerName = String(trigger.name ?? trigger.type ?? "unnamed trigger");
|
|
6404
6583
|
const where = `trigger "${triggerName}"`;
|
|
@@ -6533,7 +6712,7 @@ function registerValidatorTools(server2, client, builderClient) {
|
|
|
6533
6712
|
"validate_workflow",
|
|
6534
6713
|
"Pre-flight ID validation for a deployed GHL workflow. Scans every trigger and action for references to pipelines, pipeline stages, custom fields, users, workflows, forms, calendars, and surveys; verifies each ID exists in the current location. Use this BEFORE publish_workflow when a workflow has been edited, or whenever a published workflow stops behaving as expected. Catches the silent-failure bug where invalid IDs make GHL skip all subsequent actions without warning. Returns a structured report of valid + missing references.",
|
|
6535
6714
|
{
|
|
6536
|
-
workflowId:
|
|
6715
|
+
workflowId: import_zod43.z.string().describe("The workflow ID to validate.")
|
|
6537
6716
|
},
|
|
6538
6717
|
async ({ workflowId }) => {
|
|
6539
6718
|
try {
|
|
@@ -6901,6 +7080,8 @@ function registerAllTools(server2, client, registry2, mcpVersion) {
|
|
|
6901
7080
|
registerFormBuilderTools(server2, builderClient);
|
|
6902
7081
|
registerPipelineBuilderTools(server2, builderClient);
|
|
6903
7082
|
registerWorkflowClonerTools(server2, builderClient);
|
|
7083
|
+
registerSmartListTools(server2, builderClient);
|
|
7084
|
+
registerReputationTools(server2, builderClient);
|
|
6904
7085
|
registerValidatorTools(server2, client, builderClient);
|
|
6905
7086
|
registerDiagnosticTools(server2, mcpVersion ?? "unknown", client, builderClient, registry2 ?? null);
|
|
6906
7087
|
registerLocationSwitcherTools(server2, client, builderClient, registry2, mcpVersion);
|
|
@@ -6910,18 +7091,18 @@ function registerAllTools(server2, client, registry2, mcpVersion) {
|
|
|
6910
7091
|
var fs4 = __toESM(require("fs"));
|
|
6911
7092
|
var path4 = __toESM(require("path"));
|
|
6912
7093
|
var os = __toESM(require("os"));
|
|
6913
|
-
var
|
|
7094
|
+
var import_zod44 = require("zod");
|
|
6914
7095
|
var APP_NAME = "elitedcs-ghl-mcp";
|
|
6915
|
-
var CredentialsSchema =
|
|
6916
|
-
license_key:
|
|
6917
|
-
email:
|
|
6918
|
-
verified_at:
|
|
6919
|
-
ghl_api_key:
|
|
6920
|
-
ghl_location_id:
|
|
6921
|
-
ghl_company_id:
|
|
6922
|
-
ghl_user_id:
|
|
6923
|
-
ghl_firebase_api_key:
|
|
6924
|
-
ghl_firebase_refresh_token:
|
|
7096
|
+
var CredentialsSchema = import_zod44.z.object({
|
|
7097
|
+
license_key: import_zod44.z.string().min(1),
|
|
7098
|
+
email: import_zod44.z.string().email(),
|
|
7099
|
+
verified_at: import_zod44.z.string().min(1),
|
|
7100
|
+
ghl_api_key: import_zod44.z.string().min(1),
|
|
7101
|
+
ghl_location_id: import_zod44.z.string().min(1),
|
|
7102
|
+
ghl_company_id: import_zod44.z.string().optional(),
|
|
7103
|
+
ghl_user_id: import_zod44.z.string().optional(),
|
|
7104
|
+
ghl_firebase_api_key: import_zod44.z.string().optional(),
|
|
7105
|
+
ghl_firebase_refresh_token: import_zod44.z.string().optional()
|
|
6925
7106
|
});
|
|
6926
7107
|
function appDataDir() {
|
|
6927
7108
|
const home = os.homedir();
|
|
@@ -6971,7 +7152,7 @@ function writeCredentials(creds) {
|
|
|
6971
7152
|
// src/setup-tool.ts
|
|
6972
7153
|
var os2 = __toESM(require("os"));
|
|
6973
7154
|
var crypto3 = __toESM(require("crypto"));
|
|
6974
|
-
var
|
|
7155
|
+
var import_zod45 = require("zod");
|
|
6975
7156
|
var LICENSE_API = "https://elitedcs.com/api/validate-license";
|
|
6976
7157
|
var GHL_API = "https://services.leadconnectorhq.com";
|
|
6977
7158
|
var FIREBASE_TOKEN_API = "https://securetoken.googleapis.com/v1/token";
|
|
@@ -7040,16 +7221,16 @@ async function validateFirebase(firebaseKey, refreshToken) {
|
|
|
7040
7221
|
function registerSetupTool(server2) {
|
|
7041
7222
|
server2.tool(
|
|
7042
7223
|
"setup_ghl_mcp",
|
|
7043
|
-
"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
|
|
7224
|
+
"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 197 tools (161 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
|
|
7044
7225
|
{
|
|
7045
|
-
email:
|
|
7046
|
-
license_key:
|
|
7047
|
-
ghl_api_key:
|
|
7048
|
-
ghl_location_id:
|
|
7049
|
-
ghl_company_id:
|
|
7050
|
-
ghl_user_id:
|
|
7051
|
-
ghl_firebase_api_key:
|
|
7052
|
-
ghl_firebase_refresh_token:
|
|
7226
|
+
email: import_zod45.z.string().email().describe("Email used at purchase."),
|
|
7227
|
+
license_key: import_zod45.z.string().min(20).describe("License key from your purchase email."),
|
|
7228
|
+
ghl_api_key: import_zod45.z.string().min(10).describe("GHL Private Integration key (starts with 'pit-'). Created INSIDE the sub-account at Settings > Integrations > Private Integrations."),
|
|
7229
|
+
ghl_location_id: import_zod45.z.string().min(10).describe("GHL Location ID (sub-account ID). Found in your GHL URL: /location/THIS_PART/dashboard."),
|
|
7230
|
+
ghl_company_id: import_zod45.z.string().optional().describe("(Agency only) Company ID for multi-location access."),
|
|
7231
|
+
ghl_user_id: import_zod45.z.string().optional().describe("(Workflow Builder, optional) Firebase User ID. See README for browser capture instructions."),
|
|
7232
|
+
ghl_firebase_api_key: import_zod45.z.string().optional().describe("(Workflow Builder, optional) Firebase API Key starting with 'AIza'."),
|
|
7233
|
+
ghl_firebase_refresh_token: import_zod45.z.string().optional().describe("(Workflow Builder, optional) Firebase refresh token starting with 'AMf-'.")
|
|
7053
7234
|
},
|
|
7054
7235
|
async (args) => {
|
|
7055
7236
|
const lic = await validateLicense(args.email, args.license_key);
|
|
@@ -7095,7 +7276,7 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
|
|
|
7095
7276
|
ghl_firebase_api_key: workflowBuilderEnabled ? args.ghl_firebase_api_key?.trim() : void 0,
|
|
7096
7277
|
ghl_firebase_refresh_token: workflowBuilderEnabled ? args.ghl_firebase_refresh_token?.trim() : void 0
|
|
7097
7278
|
});
|
|
7098
|
-
const toolCount = workflowBuilderEnabled ? "
|
|
7279
|
+
const toolCount = workflowBuilderEnabled ? "197" : "161";
|
|
7099
7280
|
const wfLine = workflowBuilderEnabled ? "Workflow Builder: enabled." : "Workflow Builder: not configured (optional).";
|
|
7100
7281
|
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.";
|
|
7101
7282
|
return {
|
|
@@ -7123,11 +7304,11 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
|
|
|
7123
7304
|
function registerEnableWorkflowBuilderTool(server2) {
|
|
7124
7305
|
server2.tool(
|
|
7125
7306
|
"enable_workflow_builder",
|
|
7126
|
-
"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 161 to
|
|
7307
|
+
"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 161 to 197 after the next Claude restart.",
|
|
7127
7308
|
{
|
|
7128
|
-
ghl_user_id:
|
|
7129
|
-
ghl_firebase_api_key:
|
|
7130
|
-
ghl_firebase_refresh_token:
|
|
7309
|
+
ghl_user_id: import_zod45.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."),
|
|
7310
|
+
ghl_firebase_api_key: import_zod45.z.string().min(10).describe("Firebase API Key starting with 'AIza'. The string between 'firebase:authUser:' and ':[DEFAULT]' in the row's Key column."),
|
|
7311
|
+
ghl_firebase_refresh_token: import_zod45.z.string().min(10).describe("Firebase refresh token. value.stsTokenManager.refreshToken in the firebase:authUser row.")
|
|
7131
7312
|
},
|
|
7132
7313
|
async (args) => {
|
|
7133
7314
|
const existing = readCredentials();
|
|
@@ -7170,7 +7351,7 @@ DevTools steps: https://elitedcs.com/ghl-mcp-firebase`
|
|
|
7170
7351
|
"",
|
|
7171
7352
|
"Firebase credentials verified and saved.",
|
|
7172
7353
|
"",
|
|
7173
|
-
"**Restart Claude (quit fully and reopen) to load the workflow builder + funnel builder + pipeline builder + form builder + workflow cloner tools (
|
|
7354
|
+
"**Restart Claude (quit fully and reopen) to load the workflow builder + funnel builder + pipeline builder + form builder + workflow cloner tools (197 total).**",
|
|
7174
7355
|
"",
|
|
7175
7356
|
'After restart, try: "List my workflows in full detail" or "Validate workflow <id>".',
|
|
7176
7357
|
"",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "GoHighLevel MCP Server for Claude.
|
|
3
|
+
"version": "3.11.1",
|
|
4
|
+
"description": "GoHighLevel MCP Server for Claude. 197 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ghl-mcp": "dist/index.js"
|