@elitedcs/ghl-mcp 3.34.5 → 3.34.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,20 +1,24 @@
1
1
  # Changelog
2
2
 
3
- ## 3.34.5Update-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.
3
+ ## 3.34.6Revert v3.34.5 (internal_update_opportunity save-abort regression)
4
+
5
+ - **Reverts v3.34.5.** That release made `validateActionChain` throw on *every*
6
+ `internal_update_opportunity` action including ones round-tripped from
7
+ `get_workflow_full` unless `__customInputFields__` carried both a `pipelineId`
8
+ and a `pipelineStageId` entry, and it removed v3.34.4's id-based pass-through for
9
+ already-saved nodes. Because that check runs at the top of `buildActionChain`,
10
+ one such node aborted the **entire** `update_workflow_actions` save. Re-saving any
11
+ workflow that already contained an "Update Opportunity" / move-to-stage action
12
+ could hard-fail. The v3.34.5 normalizer was also not shape-preserving for a real
13
+ round-tripped node (it dropped extra per-field keys), risking corruption on
14
+ re-save. An independent (non-Claude) audit confirmed both failure modes.
15
+ - **v3.34.4 is retained** its merge-tag blank-render audit and the honest
16
+ Update-Opportunity guard were audited clean. Creating an
17
+ `internal_update_opportunity` from scratch is guarded again with the clear "build
18
+ that one step in the GHL UI" message; a round-tripped one passes through untouched.
19
+ - Net effect: restores the proven v3.34.3 / v3.34.4 save behavior. A correct
20
+ from-scratch builder for Update-Opportunity will be re-attempted separately, with
21
+ live verification, before any future republish.
18
22
 
19
23
  ## 3.34.4 — Audit catches blank-render merge tags + honest guard on Update-Opportunity
20
24
 
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.5",
34
+ version: "3.34.6",
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,30 +1170,6 @@ 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
- }
1197
1173
  function hasId(action) {
1198
1174
  return typeof action.id === "string" && action.id.length > 0;
1199
1175
  }
@@ -1231,10 +1207,12 @@ function validateActionChain(actions) {
1231
1207
  if (!attr.startAfter) throw new Error(`Wait action "${action.name}" missing required 'startAfter' in attributes.`);
1232
1208
  break;
1233
1209
  case "internal_update_opportunity": {
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.`);
1210
+ if (!hasId(action)) {
1211
+ throw new Error(
1212
+ `Action "${action.name}" (internal_update_opportunity / "Update Opportunity" / move-to-stage) cannot be created via the API yet: GHL's workflow builder rejects a synthesized node with "action has a corrupted type", which silently fails the whole save. Build this one step in the GHL UI (Workflow > add action > Update Opportunity), or pass through an existing one read via get_workflow_full (it keeps its id). Every other action type in this call works.`
1213
+ );
1214
+ }
1215
+ if (!Array.isArray(attr.__customInputFields__)) throw new Error(`Internal update opportunity action "${action.name}" missing '__customInputFields__' array.`);
1238
1216
  break;
1239
1217
  }
1240
1218
  case "remove_from_workflow":
@@ -1855,7 +1833,7 @@ ${errorBody}`
1855
1833
  */
1856
1834
  buildActionChain(actions) {
1857
1835
  validateActionChain(actions);
1858
- const linked = actions.map(normalizeRemoveFromWorkflowAction).map(normalizeInternalUpdateOpportunityAction).map((action, i) => {
1836
+ const linked = actions.map(normalizeRemoveFromWorkflowAction).map((action, i) => {
1859
1837
  const copy = { ...action };
1860
1838
  if (!copy.id) {
1861
1839
  copy.id = crypto.randomUUID();
@@ -5422,7 +5400,7 @@ function registerWorkflowBuilderTools(server2, client) {
5422
5400
  );
5423
5401
  server2.tool(
5424
5402
  "update_workflow_actions",
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.",
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) cannot be created fresh through this API \u2014 GHL's builder rejects a synthesized node with 'action has a corrupted type' and fails the whole save; add that single step in the GHL UI, or pass through one already read via get_workflow_full (it keeps its node id). 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.",
5426
5404
  {
5427
5405
  workflowId: import_zod33.z.string().describe("The workflow ID to update."),
5428
5406
  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.5",
3
+ "version": "3.34.6",
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": "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."
168
+ "notes": "CANNOT CREATE FRESH VIA THIS API: GHL's builder rejects a synthesized internal_update_opportunity node with 'action has a corrupted type' and fails the whole save. Build this step in the GHL UI, or round-trip one read via get_workflow_full (it keeps its node id). The shape below is correct for READING / round-tripping. 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": {