@elitedcs/ghl-mcp 3.34.4 → 3.34.5
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 +16 -0
- package/dist/index.js +31 -9
- package/package.json +1 -1
- package/templates/action-schemas.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.34.5 — Update-Opportunity actions are now creatable via the builder
|
|
4
|
+
|
|
5
|
+
- **`update_workflow_actions` can now create `internal_update_opportunity`
|
|
6
|
+
("Update Opportunity" / move-to-stage) actions from scratch.** v3.34.4 only
|
|
7
|
+
guarded against the cryptic "action has a corrupted type" save failure; this
|
|
8
|
+
release fixes the root cause. The bug: `workflowsActionType: "INTERNAL"` was
|
|
9
|
+
being placed inside `attributes`, but GHL's deserializer reads that
|
|
10
|
+
discriminator at the **node** level — a nested one is rejected as corrupted.
|
|
11
|
+
The builder now normalizes the action to GHL's real node shape
|
|
12
|
+
(`workflowsActionType` hoisted to the node level; `allowBackward` + per-field
|
|
13
|
+
`__customInputs__` scaffolding added). Captured from a real UI-built node and
|
|
14
|
+
**proven against a live builder save**. Just pass `__customInputFields__` with
|
|
15
|
+
a `pipelineId` and a `pipelineStageId` entry (validated — both required, so it
|
|
16
|
+
never saves a silent no-op). Round-tripped actions normalize idempotently. Tool
|
|
17
|
+
+ schema docs updated; the v3.34.4 "build it in the UI" guidance is superseded.
|
|
18
|
+
|
|
3
19
|
## 3.34.4 — Audit catches blank-render merge tags + honest guard on Update-Opportunity
|
|
4
20
|
|
|
5
21
|
- **`audit_workflows` / `validate_workflow` now flag dead `{{contact.X}}` merge tags.**
|
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.
|
|
34
|
+
version: "3.34.5",
|
|
35
35
|
mcpName: "io.github.drjerryrelth/ghl-command",
|
|
36
36
|
description: "GoHighLevel MCP Server for Claude. 218 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
37
37
|
main: "dist/index.js",
|
|
@@ -1170,6 +1170,30 @@ function normalizeRemoveFromWorkflowAction(action) {
|
|
|
1170
1170
|
}
|
|
1171
1171
|
return { ...action, attributes: next };
|
|
1172
1172
|
}
|
|
1173
|
+
function normalizeInternalUpdateOpportunityAction(action) {
|
|
1174
|
+
if (action.type !== "internal_update_opportunity") return action;
|
|
1175
|
+
const attrIn = action.attributes && typeof action.attributes === "object" ? action.attributes : {};
|
|
1176
|
+
const fieldsIn = Array.isArray(attrIn.__customInputFields__) ? attrIn.__customInputFields__ : [];
|
|
1177
|
+
const __customInputFields__ = fieldsIn.map((raw) => {
|
|
1178
|
+
const f = raw && typeof raw === "object" ? raw : {};
|
|
1179
|
+
return {
|
|
1180
|
+
__customInputs__: f.__customInputs__ && typeof f.__customInputs__ === "object" ? f.__customInputs__ : {},
|
|
1181
|
+
dataType: typeof f.dataType === "string" ? f.dataType : "SINGLE_OPTIONS",
|
|
1182
|
+
filterField: f.filterField,
|
|
1183
|
+
value: f.value,
|
|
1184
|
+
valueFieldType: typeof f.valueFieldType === "string" ? f.valueFieldType : "select"
|
|
1185
|
+
};
|
|
1186
|
+
});
|
|
1187
|
+
const attributes = {
|
|
1188
|
+
...attrIn,
|
|
1189
|
+
type: "internal_update_opportunity",
|
|
1190
|
+
allowBackward: typeof attrIn.allowBackward === "boolean" ? attrIn.allowBackward : false,
|
|
1191
|
+
__customInputs__: attrIn.__customInputs__ && typeof attrIn.__customInputs__ === "object" ? attrIn.__customInputs__ : {},
|
|
1192
|
+
__customInputFields__
|
|
1193
|
+
};
|
|
1194
|
+
delete attributes.workflowsActionType;
|
|
1195
|
+
return { ...action, workflowsActionType: "INTERNAL", attributes };
|
|
1196
|
+
}
|
|
1173
1197
|
function hasId(action) {
|
|
1174
1198
|
return typeof action.id === "string" && action.id.length > 0;
|
|
1175
1199
|
}
|
|
@@ -1207,12 +1231,10 @@ function validateActionChain(actions) {
|
|
|
1207
1231
|
if (!attr.startAfter) throw new Error(`Wait action "${action.name}" missing required 'startAfter' in attributes.`);
|
|
1208
1232
|
break;
|
|
1209
1233
|
case "internal_update_opportunity": {
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
}
|
|
1215
|
-
if (!Array.isArray(attr.__customInputFields__)) throw new Error(`Internal update opportunity action "${action.name}" missing '__customInputFields__' array.`);
|
|
1234
|
+
const cif = Array.isArray(attr.__customInputFields__) ? attr.__customInputFields__ : null;
|
|
1235
|
+
if (!cif) throw new Error(`Internal update opportunity action "${action.name}" missing '__customInputFields__' array (needs pipelineId + pipelineStageId entries).`);
|
|
1236
|
+
const hasTarget = (ff) => cif.some((f) => f && f.filterField === ff && typeof f.value === "string" && f.value.length > 0);
|
|
1237
|
+
if (!hasTarget("pipelineId") || !hasTarget("pipelineStageId")) throw new Error(`Internal update opportunity action "${action.name}" needs both a pipelineId and a pipelineStageId entry (each with a value) in '__customInputFields__'. Use get_pipelines to find the IDs.`);
|
|
1216
1238
|
break;
|
|
1217
1239
|
}
|
|
1218
1240
|
case "remove_from_workflow":
|
|
@@ -1833,7 +1855,7 @@ ${errorBody}`
|
|
|
1833
1855
|
*/
|
|
1834
1856
|
buildActionChain(actions) {
|
|
1835
1857
|
validateActionChain(actions);
|
|
1836
|
-
const linked = actions.map(normalizeRemoveFromWorkflowAction).map((action, i) => {
|
|
1858
|
+
const linked = actions.map(normalizeRemoveFromWorkflowAction).map(normalizeInternalUpdateOpportunityAction).map((action, i) => {
|
|
1837
1859
|
const copy = { ...action };
|
|
1838
1860
|
if (!copy.id) {
|
|
1839
1861
|
copy.id = crypto.randomUUID();
|
|
@@ -5400,7 +5422,7 @@ function registerWorkflowBuilderTools(server2, client) {
|
|
|
5400
5422
|
);
|
|
5401
5423
|
server2.tool(
|
|
5402
5424
|
"update_workflow_actions",
|
|
5403
|
-
"Update a workflow's actions (steps), triggers, name, or status. IMPORTANT: Call get_workflow_full first to see the current state before updating. Handles version tracking automatically. Uses the internal builder API (requires Firebase auth). Action types: sms, email, add_contact_tag, remove_contact_tag, wait, webhook, internal_update_opportunity, custom_code, update_contact_field, add_notes, internal_notification, task_notification, remove_from_workflow, add_to_workflow, goto, transition, workflow_goal. NOTE: internal_update_opportunity (GHL's 'Update Opportunity' / move-to-stage action)
|
|
5425
|
+
"Update a workflow's actions (steps), triggers, name, or status. IMPORTANT: Call get_workflow_full first to see the current state before updating. Handles version tracking automatically. Uses the internal builder API (requires Firebase auth). Action types: sms, email, add_contact_tag, remove_contact_tag, wait, webhook, internal_update_opportunity, custom_code, update_contact_field, add_notes, internal_notification, task_notification, remove_from_workflow, add_to_workflow, goto, transition, workflow_goal. NOTE: internal_update_opportunity (GHL's 'Update Opportunity' / move-to-stage action) is creatable \u2014 pass attributes.__customInputFields__ with a pipelineId entry and a pipelineStageId entry (use get_pipelines for the IDs); the builder normalizes it to GHL's node shape (workflowsActionType is set at the node level for you). For if/else, call build_if_else_branch and include its returned nodes; if_else is a node discriminator, not a standalone action. For goal events (exit-on-condition nodes), call build_goal_event to get a correctly-shaped workflow_goal node \u2014 wire it in by setting the prior action's `next` to the goal node's id. Trigger types: all 57 native GHL trigger types have typed validation; any unknown trigger type passes through via a permissive fallback so reads never crash.",
|
|
5404
5426
|
{
|
|
5405
5427
|
workflowId: import_zod33.z.string().describe("The workflow ID to update."),
|
|
5406
5428
|
name: import_zod33.z.string().optional().describe("New workflow name."),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.34.
|
|
3
|
+
"version": "3.34.5",
|
|
4
4
|
"mcpName": "io.github.drjerryrelth/ghl-command",
|
|
5
5
|
"description": "GoHighLevel MCP Server for Claude. 218 tools — full CRM, automation, marketing control, account-wide workflow audit, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
"workflowsActionType": "INTERNAL",
|
|
166
166
|
"type": "internal_update_opportunity"
|
|
167
167
|
},
|
|
168
|
-
"notes": "
|
|
168
|
+
"notes": "CREATABLE (v3.34.5+): pass __customInputFields__ with a pipelineId entry and a pipelineStageId entry; the builder sets workflowsActionType at the NODE level for you. (workflowsActionType must NOT be nested in attributes — GHL rejects a nested one as 'action has a corrupted type'. The builder hoists it automatically; the example here shows it in attributes only for backward-compat input.) Proven against a live builder save 2026-06-14. Use pipeline and stage IDs (not names). Use get_pipelines or list_pipelines_full to find IDs FIRST. CRITICAL: If the pipelineId or stageId don't exist in the target sub-account, GHL silently fails this action AND can kill subsequent actions in the workflow. Always verify IDs exist before deploying."
|
|
169
169
|
},
|
|
170
170
|
|
|
171
171
|
"_if_else_branching": {
|