@elitedcs/ghl-mcp 3.28.0 → 3.29.0

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/index.js +190 -42
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.29.0 — Documents & Contracts API fix + workflow/contact bug fixes
4
+
5
+ Five confirmed bugs fixed, verified live against the GHL API.
6
+
7
+ **Documents & Contracts (the big one).** The document tools were hitting a
8
+ non-existent `/documents/` path and returning 404. GHL's real Documents &
9
+ Contracts API lives under `/proposals/*`:
10
+
11
+ - `list_documents` now calls `GET /proposals/document` with real filters:
12
+ `status` (draft/sent/viewed/completed/declined), `paymentStatus`, `query`,
13
+ `dateFrom`/`dateTo`, and pagination (`limit` capped at GHL's max of 21, `skip`).
14
+ - `get_document` resolves a document by id by scanning the list (GHL exposes no
15
+ public get-by-id route).
16
+ - `send_document` now calls `POST /proposals/document/send` to dispatch an
17
+ existing document to its recipients.
18
+ - `delete_document` reports honestly that GHL's public API has no delete/void
19
+ route (do it in the UI) instead of failing on a dead path.
20
+ - **New:** `list_document_templates` (`GET /proposals/templates`) lists your
21
+ reusable contract templates.
22
+ - **New:** `send_document_template` (`POST /proposals/templates/send`) creates
23
+ and sends a contract to a contact from a template.
24
+
25
+ **Workflow triggers.** `get_workflow_full` and `update_workflow_actions` no
26
+ longer crash on the Documents & Contracts trigger (`proposal_estimate_update`,
27
+ `masterType: "internal"`). The trigger union now accepts any `masterType` so
28
+ reads never throw, and `proposal_estimate_update` has typed support.
29
+
30
+ **Workflow settings preserved.** `update_workflow_actions` previously reset
31
+ `allowMultiple`, `stopOnResponse`, `autoMarkAsRead`,
32
+ `removeContactFromLastStep`, and `allowMultipleOpportunity` to defaults on every
33
+ save. It now preserves the workflow's current values, and exposes all five as
34
+ optional parameters.
35
+
36
+ **Contact search.** `search_contacts` now advances pages correctly (sends both
37
+ `startAfter` and `startAfterId` cursors), and uses the correct sort parameters:
38
+ `order` (asc/desc) instead of the rejected `sortOrder`, with `sortBy` limited to
39
+ `date_added`/`date_updated`.
40
+
3
41
  ## 3.28.0 — Tool allowlist (issue #1): cut context cost on big installs
4
42
 
5
43
  Two new optional env vars let you gate which tools register at startup so
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.28.0",
34
+ version: "3.29.0",
35
35
  mcpName: "io.github.drjerryrelth/ghl-command",
36
36
  description: "GoHighLevel MCP Server for Claude. 212 tools \u2014 full CRM, automation, marketing control, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
37
37
  main: "dist/index.js",
@@ -682,7 +682,11 @@ var TriggerCommonSchema = import_zod3.z.object({
682
682
  origin_id: import_zod3.z.string().optional(),
683
683
  active: import_zod3.z.boolean().optional(),
684
684
  workflow_id: import_zod3.z.string().optional(),
685
- masterType: import_zod3.z.literal("highlevel").optional(),
685
+ // Most native GHL triggers report masterType "highlevel", but some
686
+ // first-party features use other values (e.g. Documents & Contracts uses
687
+ // "internal"). Accept any string so reads never crash on the masterType
688
+ // discriminator and writes round-trip the original value unchanged.
689
+ masterType: import_zod3.z.string().optional(),
686
690
  name: import_zod3.z.string().optional(),
687
691
  actions: import_zod3.z.array(TriggerActionSchema).optional(),
688
692
  schedule_config: import_zod3.z.record(import_zod3.z.unknown()).optional(),
@@ -987,6 +991,19 @@ var CustomObjectChangedTriggerSchema = TriggerCommonSchema.extend({
987
991
  });
988
992
  var ConvAiTriggerTriggerSchema = typedTrigger("conv_ai_trigger", [], "fieldless");
989
993
  var ConvAiAutonomousTriggerTriggerSchema = typedTrigger("conv_ai_autonomous_trigger", [], "fieldless");
994
+ var ProposalEstimateUpdateTriggerSchema = TriggerCommonSchema.extend({
995
+ type: import_zod3.z.literal("proposal_estimate_update"),
996
+ conditions: import_zod3.z.array(import_zod3.z.object({
997
+ operator: import_zod3.z.string().optional(),
998
+ field: import_zod3.z.string().optional().describe(
999
+ "Documents & Contracts filter. Common fields: a document/estimate status path (values like sent, viewed, signed, completed, declined) and the document/template id. Exact field paths are not fully captured \u2014 pass through whatever the GHL UI shows."
1000
+ ),
1001
+ value: import_zod3.z.unknown().optional(),
1002
+ title: import_zod3.z.string().optional(),
1003
+ type: import_zod3.z.string().optional(),
1004
+ id: import_zod3.z.string().optional()
1005
+ }).passthrough()).optional()
1006
+ });
990
1007
  var UnknownTriggerSchema = TriggerCommonSchema.extend({
991
1008
  type: import_zod3.z.string(),
992
1009
  conditions: import_zod3.z.array(import_zod3.z.record(import_zod3.z.unknown())).optional()
@@ -1046,6 +1063,7 @@ var WorkflowTriggerSchema = import_zod3.z.union([
1046
1063
  OrderSubmissionTriggerSchema,
1047
1064
  ConvAiTriggerTriggerSchema,
1048
1065
  ConvAiAutonomousTriggerTriggerSchema,
1066
+ ProposalEstimateUpdateTriggerSchema,
1049
1067
  CustomObjectCreatedTriggerSchema,
1050
1068
  CustomObjectChangedTriggerSchema,
1051
1069
  FacebookLeadGenTriggerSchema,
@@ -1635,11 +1653,14 @@ ${errorBody}`
1635
1653
  version: current.version,
1636
1654
  dataVersion: current.dataVersion ?? 1,
1637
1655
  timezone: current.timezone ?? "account",
1638
- stopOnResponse: false,
1639
- allowMultiple: false,
1640
- allowMultipleOpportunity: false,
1641
- autoMarkAsRead: false,
1642
- removeContactFromLastStep: true,
1656
+ // Preserve the workflow's existing settings unless explicitly overridden.
1657
+ // These were previously hardcoded, which silently reset re-enrollment
1658
+ // (allowMultiple) and other toggles on every action update.
1659
+ stopOnResponse: updates.stopOnResponse ?? current.stopOnResponse ?? false,
1660
+ allowMultiple: updates.allowMultiple ?? current.allowMultiple ?? false,
1661
+ allowMultipleOpportunity: updates.allowMultipleOpportunity ?? current.allowMultipleOpportunity ?? false,
1662
+ autoMarkAsRead: updates.autoMarkAsRead ?? current.autoMarkAsRead ?? false,
1663
+ removeContactFromLastStep: updates.removeContactFromLastStep ?? current.removeContactFromLastStep ?? true,
1643
1664
  workflowData: { templates: linkedActions },
1644
1665
  updatedBy: this.userId,
1645
1666
  // Triggers live in Firestore and are managed out-of-band; the workflow
@@ -1958,20 +1979,25 @@ function registerContactTools(server2, client) {
1958
1979
  locationId: import_zod6.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env."),
1959
1980
  query: import_zod6.z.string().optional().describe("Search query to filter contacts (searches name, email, phone, etc.)."),
1960
1981
  limit: import_zod6.z.number().optional().describe("Maximum number of contacts to return. Defaults to 20."),
1961
- startAfterId: import_zod6.z.string().optional().describe("Contact ID to start after, for cursor-based pagination."),
1962
- sortBy: import_zod6.z.string().optional().describe("Field to sort results by (e.g. 'dateAdded', 'name')."),
1963
- sortOrder: import_zod6.z.string().optional().describe("Sort direction: 'asc' or 'desc'.")
1982
+ startAfter: import_zod6.z.union([import_zod6.z.number(), import_zod6.z.string()]).optional().describe("Timestamp cursor (ms epoch) from the previous page's meta.startAfter. GHL cursor pagination needs BOTH startAfter and startAfterId to advance \u2014 pass both together."),
1983
+ startAfterId: import_zod6.z.string().optional().describe("Contact ID cursor from the previous page's meta.startAfterId. Use together with startAfter to advance pages."),
1984
+ sortBy: import_zod6.z.enum(["date_added", "date_updated"]).optional().describe("Field to sort by. GHL only accepts snake_case 'date_added' or 'date_updated'."),
1985
+ order: import_zod6.z.enum(["asc", "desc"]).optional().describe("Sort direction: 'asc' or 'desc'. (GHL rejects the legacy 'sortOrder' name \u2014 this maps to the 'order' query param.)")
1964
1986
  },
1965
- async ({ locationId: locationId2, query, limit, startAfterId, sortBy, sortOrder }) => {
1987
+ async ({ locationId: locationId2, query, limit, startAfter, startAfterId, sortBy, order }) => {
1966
1988
  const resolvedLocationId = client.resolveLocationId(locationId2);
1967
1989
  const raw = await client.get("/contacts/", {
1968
1990
  params: {
1969
1991
  locationId: resolvedLocationId,
1970
1992
  query,
1971
1993
  limit: limit ?? 20,
1994
+ // Cursor pagination requires both the timestamp and the id; the GHL
1995
+ // nextPageUrl always carries both. Passing only startAfterId returns
1996
+ // the same page.
1997
+ startAfter,
1972
1998
  startAfterId,
1973
1999
  sortBy,
1974
- sortOrder
2000
+ order
1975
2001
  }
1976
2002
  });
1977
2003
  return ContactSearchResponseSchema.parse(raw);
@@ -4914,23 +4940,35 @@ function registerWebhookTools(server2, client) {
4914
4940
 
4915
4941
  // src/tools/documents.ts
4916
4942
  var import_zod32 = require("zod");
4943
+ var DOC_PAGE_MAX = 21;
4944
+ var DOC_SCAN_MAX_PAGES = 200;
4917
4945
  function registerDocumentTools(server2, client) {
4918
4946
  safeTool(
4919
4947
  server2,
4920
4948
  "list_documents",
4921
- "List documents and contracts in a GHL location. Supports pagination.",
4949
+ "List Documents & Contracts (sent/draft/completed agreements) in a GHL location. Filter by signature status and payment status. Backed by GET /proposals/document.",
4922
4950
  {
4923
4951
  locationId: import_zod32.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env."),
4924
- limit: import_zod32.z.number().optional().describe("Maximum documents to return. Defaults to 20."),
4925
- offset: import_zod32.z.number().optional().describe("Number of records to skip for pagination.")
4926
- },
4927
- async ({ locationId: locationId2, limit, offset }) => {
4952
+ status: import_zod32.z.enum(["draft", "sent", "viewed", "completed", "declined", "expired", "voided"]).optional().describe("Filter by document signature status (e.g. 'sent', 'completed', 'draft'). Server-side filter."),
4953
+ paymentStatus: import_zod32.z.string().optional().describe("Filter by payment status (e.g. 'paid', 'pending', 'no_payment')."),
4954
+ query: import_zod32.z.string().optional().describe("Free-text search across document names/recipients."),
4955
+ dateFrom: import_zod32.z.string().optional().describe("Only documents updated on/after this date (ISO 8601)."),
4956
+ dateTo: import_zod32.z.string().optional().describe("Only documents updated on/before this date (ISO 8601)."),
4957
+ limit: import_zod32.z.number().optional().describe("Max documents to return. Defaults to 20. GHL caps this at 21 per page."),
4958
+ skip: import_zod32.z.number().optional().describe("Number of records to skip for pagination.")
4959
+ },
4960
+ async ({ locationId: locationId2, status, paymentStatus, query, dateFrom, dateTo, limit, skip }) => {
4928
4961
  const resolvedLocationId = client.resolveLocationId(locationId2);
4929
- return client.get(`/documents/`, {
4962
+ return client.get(`/proposals/document`, {
4930
4963
  params: {
4931
4964
  locationId: resolvedLocationId,
4932
- limit: limit ?? 20,
4933
- offset
4965
+ status,
4966
+ paymentStatus,
4967
+ query,
4968
+ dateFrom,
4969
+ dateTo,
4970
+ limit: Math.min(limit ?? 20, DOC_PAGE_MAX),
4971
+ skip
4934
4972
  }
4935
4973
  });
4936
4974
  }
@@ -4938,47 +4976,147 @@ function registerDocumentTools(server2, client) {
4938
4976
  safeTool(
4939
4977
  server2,
4940
4978
  "get_document",
4941
- "Retrieve a single document or contract by its ID, including signature status.",
4979
+ "Retrieve a single Document & Contract by its ID, including signature/payment status and recipients. NOTE: GHL's public API has no get-by-id route, so this scans the documents list (GET /proposals/document) to find a match.",
4942
4980
  {
4943
- documentId: import_zod32.z.string().describe("The document ID to retrieve."),
4981
+ documentId: import_zod32.z.string().describe("The document ID to retrieve (the document's _id)."),
4944
4982
  locationId: import_zod32.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env.")
4945
4983
  },
4946
4984
  async ({ documentId, locationId: locationId2 }) => {
4947
4985
  const resolvedLocationId = client.resolveLocationId(locationId2);
4948
- return client.get(`/documents/${documentId}`, {
4949
- params: { locationId: resolvedLocationId }
4950
- });
4986
+ let skip = 0;
4987
+ let total = Infinity;
4988
+ let scannedAll = true;
4989
+ for (let page = 0; page < DOC_SCAN_MAX_PAGES && skip < total; page++) {
4990
+ const res = await client.get(`/proposals/document`, {
4991
+ params: { locationId: resolvedLocationId, limit: DOC_PAGE_MAX, skip }
4992
+ });
4993
+ const docs = res?.documents ?? [];
4994
+ if (typeof res?.total === "number") total = res.total;
4995
+ const match = docs.find(
4996
+ (d) => d._id === documentId || d.documentId === documentId || d.id === documentId
4997
+ );
4998
+ if (match) return match;
4999
+ if (docs.length < DOC_PAGE_MAX) break;
5000
+ skip += DOC_PAGE_MAX;
5001
+ if (page === DOC_SCAN_MAX_PAGES - 1 && skip < total) scannedAll = false;
5002
+ }
5003
+ return {
5004
+ found: false,
5005
+ documentId,
5006
+ message: scannedAll ? `No document with id ${documentId} found in location ${resolvedLocationId}. Use list_documents to browse available documents.` : `No document with id ${documentId} found within the first ${DOC_SCAN_MAX_PAGES * DOC_PAGE_MAX} documents scanned. This location has more; narrow the set with list_documents filters (status, query, date range).`
5007
+ };
5008
+ }
5009
+ );
5010
+ safeTool(
5011
+ server2,
5012
+ "send_document",
5013
+ "Send/dispatch an EXISTING Document & Contract to the recipients already on it. To create and send a new contract to a contact, use send_document_template instead. Backed by POST /proposals/document/send.",
5014
+ {
5015
+ documentId: import_zod32.z.string().describe("The document ID to send."),
5016
+ sentBy: import_zod32.z.string().optional().describe("GHL user ID of the sender (required by the API; falls back to GHL_USER_ID env)."),
5017
+ documentName: import_zod32.z.string().optional().describe("Override the document name shown to recipients."),
5018
+ medium: import_zod32.z.string().optional().describe("Delivery medium, e.g. 'email'. Defaults to email."),
5019
+ ccRecipients: import_zod32.z.array(import_zod32.z.record(import_zod32.z.unknown())).optional().describe("Optional CC recipients: [{id, email, contactName, firstName, lastName, imageUrl}]."),
5020
+ notificationSettings: import_zod32.z.record(import_zod32.z.unknown()).optional().describe("Optional sender/receiver notification settings: {sender:{fromName,fromEmail}, receive:{subject,templateId}}."),
5021
+ locationId: import_zod32.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env.")
5022
+ },
5023
+ async ({ documentId, sentBy, documentName, medium, ccRecipients, notificationSettings, locationId: locationId2 }) => {
5024
+ const resolvedLocationId = client.resolveLocationId(locationId2);
5025
+ const resolvedSentBy = sentBy ?? process.env.GHL_USER_ID;
5026
+ if (!resolvedSentBy) {
5027
+ throw new Error(
5028
+ "sentBy (sender user ID) is required. Provide it as a parameter or set GHL_USER_ID in your env."
5029
+ );
5030
+ }
5031
+ const body = {
5032
+ locationId: resolvedLocationId,
5033
+ documentId,
5034
+ sentBy: resolvedSentBy
5035
+ };
5036
+ if (documentName !== void 0) body.documentName = documentName;
5037
+ if (medium !== void 0) body.medium = medium;
5038
+ if (ccRecipients !== void 0) body.ccRecipients = ccRecipients;
5039
+ if (notificationSettings !== void 0) body.notificationSettings = notificationSettings;
5040
+ return client.post(`/proposals/document/send`, { body });
4951
5041
  }
4952
5042
  );
4953
5043
  safeTool(
4954
5044
  server2,
4955
5045
  "delete_document",
4956
- "Delete a document or contract by ID.",
5046
+ "Delete or void a Document & Contract. NOTE: GHL's public API does NOT support deleting or voiding documents \u2014 this must be done in the GHL UI (Payments \u2192 Documents & Contracts \u2192 the document's \u22EF menu \u2192 Delete/Void). This tool reports that limitation rather than failing silently.",
4957
5047
  {
4958
- documentId: import_zod32.z.string().describe("The document ID to delete."),
5048
+ documentId: import_zod32.z.string().describe("The document ID you intend to delete/void."),
4959
5049
  locationId: import_zod32.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env.")
4960
5050
  },
4961
- async ({ documentId, locationId: locationId2 }) => {
5051
+ async ({ documentId }) => {
5052
+ return {
5053
+ supported: false,
5054
+ documentId,
5055
+ message: "GHL's public Documents & Contracts API does not expose a delete or void/recall endpoint. Delete or void this document in the GHL UI: Payments \u2192 Documents & Contracts \u2192 open the document \u2192 \u22EF menu \u2192 Delete or Void."
5056
+ };
5057
+ }
5058
+ );
5059
+ safeTool(
5060
+ server2,
5061
+ "list_document_templates",
5062
+ "List Documents & Contracts TEMPLATES (the reusable contract templates you build, e.g. agreements you send to clients). Backed by GET /proposals/templates.",
5063
+ {
5064
+ locationId: import_zod32.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env."),
5065
+ type: import_zod32.z.string().optional().describe("Filter by template type (e.g. 'proposal')."),
5066
+ name: import_zod32.z.string().optional().describe("Filter by template name (partial match)."),
5067
+ isPublicDocument: import_zod32.z.boolean().optional().describe("Filter for public templates only."),
5068
+ userId: import_zod32.z.string().optional().describe("Filter by the user who created the template."),
5069
+ dateFrom: import_zod32.z.string().optional().describe("Only templates updated on/after this date (ISO 8601)."),
5070
+ dateTo: import_zod32.z.string().optional().describe("Only templates updated on/before this date (ISO 8601)."),
5071
+ limit: import_zod32.z.number().optional().describe("Max templates to return. Defaults to 20."),
5072
+ skip: import_zod32.z.number().optional().describe("Number of records to skip for pagination.")
5073
+ },
5074
+ async ({ locationId: locationId2, type, name, isPublicDocument, userId, dateFrom, dateTo, limit, skip }) => {
4962
5075
  const resolvedLocationId = client.resolveLocationId(locationId2);
4963
- return client.delete(`/documents/${documentId}`, {
4964
- params: { locationId: resolvedLocationId }
5076
+ return client.get(`/proposals/templates`, {
5077
+ params: {
5078
+ locationId: resolvedLocationId,
5079
+ type,
5080
+ name,
5081
+ isPublicDocument,
5082
+ userId,
5083
+ dateFrom,
5084
+ dateTo,
5085
+ limit: limit ?? 20,
5086
+ skip
5087
+ }
4965
5088
  });
4966
5089
  }
4967
5090
  );
4968
5091
  safeTool(
4969
5092
  server2,
4970
- "send_document",
4971
- "Send a document/contract to a contact for electronic signature.",
5093
+ "send_document_template",
5094
+ "Create and send a new Document & Contract to a contact from a template. This is how you send a contract to someone. Backed by POST /proposals/templates/send.",
4972
5095
  {
4973
- documentId: import_zod32.z.string().describe("The document ID to send."),
4974
- contactId: import_zod32.z.string().describe("The contact ID to send the document to."),
5096
+ templateId: import_zod32.z.string().describe("The template ID to send (from list_document_templates)."),
5097
+ contactId: import_zod32.z.string().describe("The contact ID to send the contract to."),
5098
+ userId: import_zod32.z.string().optional().describe("GHL user ID of the sender (required by the API; falls back to GHL_USER_ID env)."),
5099
+ sendDocument: import_zod32.z.boolean().optional().describe("If true (default), actually dispatch the document. If false, only create the draft."),
5100
+ opportunityId: import_zod32.z.string().optional().describe("Optionally link the document to an opportunity."),
4975
5101
  locationId: import_zod32.z.string().optional().describe("GHL Location ID. Optional if GHL_LOCATION_ID is set in env.")
4976
5102
  },
4977
- async ({ documentId, contactId, locationId: locationId2 }) => {
5103
+ async ({ templateId, contactId, userId, sendDocument, opportunityId, locationId: locationId2 }) => {
4978
5104
  const resolvedLocationId = client.resolveLocationId(locationId2);
4979
- return client.post(`/documents/${documentId}/send`, {
4980
- body: { locationId: resolvedLocationId, contactId }
4981
- });
5105
+ const resolvedUserId = userId ?? process.env.GHL_USER_ID;
5106
+ if (!resolvedUserId) {
5107
+ throw new Error(
5108
+ "userId (sender user ID) is required. Provide it as a parameter or set GHL_USER_ID in your env."
5109
+ );
5110
+ }
5111
+ const body = {
5112
+ templateId,
5113
+ contactId,
5114
+ userId: resolvedUserId,
5115
+ locationId: resolvedLocationId
5116
+ };
5117
+ if (sendDocument !== void 0) body.sendDocument = sendDocument;
5118
+ if (opportunityId !== void 0) body.opportunityId = opportunityId;
5119
+ return client.post(`/proposals/templates/send`, { body });
4982
5120
  }
4983
5121
  );
4984
5122
  }
@@ -5135,15 +5273,25 @@ function registerWorkflowBuilderTools(server2, client) {
5135
5273
  name: import_zod33.z.string().optional().describe("New workflow name."),
5136
5274
  status: import_zod33.z.string().optional().describe("New status: 'draft' or 'published'."),
5137
5275
  actions: import_zod33.z.array(ActionSchema).optional().describe("Array of workflow actions/steps. For linear flows, provide in order \u2014 chaining is automatic."),
5138
- triggers: import_zod33.z.array(WorkflowTriggerSchema).optional().describe("Array of workflow triggers.")
5139
- },
5140
- async ({ workflowId, name, status, actions, triggers }) => {
5276
+ triggers: import_zod33.z.array(WorkflowTriggerSchema).optional().describe("Array of workflow triggers."),
5277
+ allowMultiple: import_zod33.z.boolean().optional().describe("Allow a contact to be enrolled more than once (re-enrollment). If omitted, the workflow's current value is preserved."),
5278
+ stopOnResponse: import_zod33.z.boolean().optional().describe("Stop the workflow when the contact replies. If omitted, the current value is preserved."),
5279
+ autoMarkAsRead: import_zod33.z.boolean().optional().describe("Auto-mark conversations as read. If omitted, the current value is preserved."),
5280
+ removeContactFromLastStep: import_zod33.z.boolean().optional().describe("Remove the contact when they reach the last step. If omitted, the current value is preserved."),
5281
+ allowMultipleOpportunity: import_zod33.z.boolean().optional().describe("Allow creating multiple opportunities. If omitted, the current value is preserved.")
5282
+ },
5283
+ async ({ workflowId, name, status, actions, triggers, allowMultiple, stopOnResponse, autoMarkAsRead, removeContactFromLastStep, allowMultipleOpportunity }) => {
5141
5284
  try {
5142
5285
  const result = await client.updateWorkflow(workflowId, {
5143
5286
  name,
5144
5287
  status,
5145
5288
  actions,
5146
- triggers
5289
+ triggers,
5290
+ allowMultiple,
5291
+ stopOnResponse,
5292
+ autoMarkAsRead,
5293
+ removeContactFromLastStep,
5294
+ allowMultipleOpportunity
5147
5295
  });
5148
5296
  return jsonResponse(result);
5149
5297
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.28.0",
3
+ "version": "3.29.0",
4
4
  "mcpName": "io.github.drjerryrelth/ghl-command",
5
5
  "description": "GoHighLevel MCP Server for Claude. 212 tools — full CRM, automation, marketing control, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
6
6
  "main": "dist/index.js",