@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 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.4",
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
- 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.`);
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) 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.",
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.4",
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": "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."
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": {