@elitedcs/ghl-mcp 3.39.0 → 3.40.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.
- package/dist/index.js +201 -8
- package/package.json +1 -1
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
|
+
version: "3.40.0",
|
|
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",
|
|
@@ -10921,6 +10921,18 @@ function validateBuildPlan(input) {
|
|
|
10921
10921
|
seen.add(key);
|
|
10922
10922
|
}
|
|
10923
10923
|
}
|
|
10924
|
+
for (const fn of plan.funnels ?? []) {
|
|
10925
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10926
|
+
for (const pg of fn.pages) {
|
|
10927
|
+
const key = pg.name.trim().toLowerCase();
|
|
10928
|
+
if (seen.has(key)) {
|
|
10929
|
+
allErrors.push(
|
|
10930
|
+
`funnel "${fn.ref}" has duplicate page name "${pg.name}" \u2014 page names must be unique within a funnel so each page ref resolves to a single step`
|
|
10931
|
+
);
|
|
10932
|
+
}
|
|
10933
|
+
seen.add(key);
|
|
10934
|
+
}
|
|
10935
|
+
}
|
|
10924
10936
|
const nameGroups = [
|
|
10925
10937
|
["pipelines", (plan.pipelines ?? []).map((x) => ({ ref: x.ref, name: x.name }))],
|
|
10926
10938
|
["customFields", (plan.customFields ?? []).map((x) => ({ ref: x.ref, name: x.name }))],
|
|
@@ -11437,6 +11449,10 @@ function renderReport(plan, result, ctx) {
|
|
|
11437
11449
|
any = true;
|
|
11438
11450
|
L.push(` \u2022 [calendar] ${c.reason}`);
|
|
11439
11451
|
}
|
|
11452
|
+
for (const fn of plan.funnels ?? []) {
|
|
11453
|
+
any = true;
|
|
11454
|
+
L.push(` \u2022 [funnel] Design + populate the pages of funnel "${fn.name}" (Blueprint builds the funnel + steps; page content/design is manual).`);
|
|
11455
|
+
}
|
|
11440
11456
|
for (const w of result.workflows) {
|
|
11441
11457
|
for (const m of w.manual) {
|
|
11442
11458
|
any = true;
|
|
@@ -11459,6 +11475,9 @@ function renderReport(plan, result, ctx) {
|
|
|
11459
11475
|
// src/intake-to-build/execute.ts
|
|
11460
11476
|
var norm2 = (s) => s.trim().toLowerCase();
|
|
11461
11477
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
11478
|
+
function slugifyName(s) {
|
|
11479
|
+
return s.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean).join("-");
|
|
11480
|
+
}
|
|
11462
11481
|
async function executeBackbone(plan, deps, opts = {}) {
|
|
11463
11482
|
const retries = opts.verifyRetries ?? 4;
|
|
11464
11483
|
const backoff = opts.verifyBackoffMs ?? 500;
|
|
@@ -11667,6 +11686,85 @@ async function executeBackbone(plan, deps, opts = {}) {
|
|
|
11667
11686
|
idMap[form.ref] = formId;
|
|
11668
11687
|
built.push({ ref: form.ref, type: "form", name: form.name, status: "created", realId: formId });
|
|
11669
11688
|
}
|
|
11689
|
+
const formNameByRef = new Map((plan.forms ?? []).map((f) => [f.ref, f.name]));
|
|
11690
|
+
const calNameByRef = new Map((plan.calendars ?? []).map((c) => [c.ref, c.name]));
|
|
11691
|
+
const funnelContentReason = (fn) => {
|
|
11692
|
+
const pageBits = fn.pages.map((pg) => {
|
|
11693
|
+
const hosts = [];
|
|
11694
|
+
if (pg.formRef) hosts.push(`hosts form "${formNameByRef.get(pg.formRef) ?? pg.formRef}"`);
|
|
11695
|
+
if (pg.calendarRef) hosts.push(`links calendar "${calNameByRef.get(pg.calendarRef) ?? pg.calendarRef}"`);
|
|
11696
|
+
return `page "${pg.name}"${hosts.length ? ` (${hosts.join(", ")})` : ""}`;
|
|
11697
|
+
});
|
|
11698
|
+
return `Design + publish the pages of funnel "${fn.name}": ${pageBits.join("; ")}. (Blueprint built the funnel + steps; page content/design is manual.)`;
|
|
11699
|
+
};
|
|
11700
|
+
for (const fn of plan.funnels ?? []) {
|
|
11701
|
+
let funnels;
|
|
11702
|
+
try {
|
|
11703
|
+
funnels = await deps.listFunnels();
|
|
11704
|
+
} catch (e) {
|
|
11705
|
+
return halt(fn.ref, "funnel", `could not read existing funnels: ${msg(e)}`);
|
|
11706
|
+
}
|
|
11707
|
+
const matches = funnels.filter((f) => norm2(f.name) === norm2(fn.name));
|
|
11708
|
+
if (matches.length > 1) {
|
|
11709
|
+
return halt(fn.ref, "funnel", `${matches.length} existing funnels are named "${fn.name}" \u2014 ambiguous, cannot safely bind ${fn.ref}. Resolve the duplicate in GHL, then re-run.`);
|
|
11710
|
+
}
|
|
11711
|
+
if (matches.length === 1) {
|
|
11712
|
+
const ex = matches[0];
|
|
11713
|
+
const pageBindings = [];
|
|
11714
|
+
const problems = [];
|
|
11715
|
+
for (const pg of fn.pages) {
|
|
11716
|
+
const stepMatches = ex.steps.filter((s) => norm2(s.name) === norm2(pg.name));
|
|
11717
|
+
if (stepMatches.length === 1) pageBindings.push({ ref: pg.ref, name: pg.name, id: stepMatches[0].id });
|
|
11718
|
+
else if (stepMatches.length === 0) problems.push(`page "${pg.name}" has no matching step`);
|
|
11719
|
+
else problems.push(`page "${pg.name}" matches ${stepMatches.length} steps`);
|
|
11720
|
+
}
|
|
11721
|
+
if (problems.length) {
|
|
11722
|
+
return halt(fn.ref, "funnel", `a funnel named "${fn.name}" already exists but its steps don't match the plan: ${problems.join("; ")}. Align the steps in GHL or remove the funnel, then re-run. (Blueprint never modifies an existing funnel.)`);
|
|
11723
|
+
}
|
|
11724
|
+
idMap[fn.ref] = ex.id;
|
|
11725
|
+
built.push({ ref: fn.ref, type: "funnel", name: fn.name, status: "existing", realId: ex.id });
|
|
11726
|
+
for (const b of pageBindings) {
|
|
11727
|
+
idMap[b.ref] = b.id;
|
|
11728
|
+
built.push({ ref: b.ref, type: "page", name: b.name, status: "existing", realId: b.id });
|
|
11729
|
+
}
|
|
11730
|
+
manual.push({ ref: fn.ref, type: "funnel-page-content", name: fn.name, reason: funnelContentReason(fn) });
|
|
11731
|
+
continue;
|
|
11732
|
+
}
|
|
11733
|
+
let funnelId;
|
|
11734
|
+
try {
|
|
11735
|
+
funnelId = await deps.createFunnel(fn.name);
|
|
11736
|
+
} catch (e) {
|
|
11737
|
+
return halt(fn.ref, "funnel", `create failed: ${msg(e)}`);
|
|
11738
|
+
}
|
|
11739
|
+
if (!funnelId) return halt(fn.ref, "funnel", "funnel create returned no id");
|
|
11740
|
+
const stepBindings = [];
|
|
11741
|
+
try {
|
|
11742
|
+
for (const pg of fn.pages) {
|
|
11743
|
+
const stepId = await deps.createFunnelStep(funnelId, pg.name, `/${slugifyName(pg.name)}`);
|
|
11744
|
+
if (!stepId) throw new Error(`step "${pg.name}" returned no id`);
|
|
11745
|
+
stepBindings.push({ ref: pg.ref, name: pg.name, id: stepId });
|
|
11746
|
+
}
|
|
11747
|
+
} catch (e) {
|
|
11748
|
+
let rolledBack = false;
|
|
11749
|
+
try {
|
|
11750
|
+
await deps.deleteFunnel(funnelId);
|
|
11751
|
+
rolledBack = true;
|
|
11752
|
+
} catch {
|
|
11753
|
+
}
|
|
11754
|
+
return halt(
|
|
11755
|
+
fn.ref,
|
|
11756
|
+
"funnel",
|
|
11757
|
+
rolledBack ? `step create failed; the partial funnel was rolled back (deleted): ${msg(e)}` : `step create failed AND the rollback delete also failed \u2014 manually delete the orphan funnel (id ${funnelId}) in GHL before re-running: ${msg(e)}`
|
|
11758
|
+
);
|
|
11759
|
+
}
|
|
11760
|
+
idMap[fn.ref] = funnelId;
|
|
11761
|
+
built.push({ ref: fn.ref, type: "funnel", name: fn.name, status: "created", realId: funnelId });
|
|
11762
|
+
for (const sb of stepBindings) {
|
|
11763
|
+
idMap[sb.ref] = sb.id;
|
|
11764
|
+
built.push({ ref: sb.ref, type: "page", name: sb.name, status: "created", realId: sb.id });
|
|
11765
|
+
}
|
|
11766
|
+
manual.push({ ref: fn.ref, type: "funnel-page-content", name: fn.name, reason: funnelContentReason(fn) });
|
|
11767
|
+
}
|
|
11670
11768
|
return { ok: true, idMap, built, manual, deferred: deferredSections(plan) };
|
|
11671
11769
|
}
|
|
11672
11770
|
async function buildSimple(objects, type, list, create, nameOf, pollForNew, idMap, built) {
|
|
@@ -11703,7 +11801,6 @@ async function buildSimple(objects, type, list, create, nameOf, pollForNew, idMa
|
|
|
11703
11801
|
}
|
|
11704
11802
|
function deferredSections(plan) {
|
|
11705
11803
|
const out = [];
|
|
11706
|
-
if (plan.funnels?.length) out.push({ section: "funnels", count: plan.funnels.length });
|
|
11707
11804
|
if (plan.workflows?.length) out.push({ section: "workflows", count: plan.workflows.length });
|
|
11708
11805
|
return out;
|
|
11709
11806
|
}
|
|
@@ -11840,6 +11937,38 @@ function pickPipelines(raw) {
|
|
|
11840
11937
|
}
|
|
11841
11938
|
return out;
|
|
11842
11939
|
}
|
|
11940
|
+
function pickFunnels(raw) {
|
|
11941
|
+
const root = raw && typeof raw === "object" ? raw : {};
|
|
11942
|
+
const list = Array.isArray(root.funnels) ? root.funnels : Array.isArray(raw) ? raw : [];
|
|
11943
|
+
const out = [];
|
|
11944
|
+
for (const item of list) {
|
|
11945
|
+
if (!item || typeof item !== "object") continue;
|
|
11946
|
+
const f = item;
|
|
11947
|
+
const id = typeof f._id === "string" ? f._id : typeof f.id === "string" ? f.id : void 0;
|
|
11948
|
+
const name = typeof f.name === "string" ? f.name : void 0;
|
|
11949
|
+
if (!id || !name) continue;
|
|
11950
|
+
const stepsRaw = Array.isArray(f.steps) ? f.steps : [];
|
|
11951
|
+
const steps = stepsRaw.filter((s) => !!s && typeof s === "object").map((s) => ({
|
|
11952
|
+
id: typeof s._id === "string" ? s._id : typeof s.id === "string" ? s.id : "",
|
|
11953
|
+
name: typeof s.name === "string" ? s.name : ""
|
|
11954
|
+
})).filter((s) => s.id && s.name);
|
|
11955
|
+
out.push({ id, name, steps });
|
|
11956
|
+
}
|
|
11957
|
+
return out;
|
|
11958
|
+
}
|
|
11959
|
+
function extractFunnelId(result) {
|
|
11960
|
+
if (!result || typeof result !== "object") return void 0;
|
|
11961
|
+
const r = result;
|
|
11962
|
+
if (typeof r._id === "string") return r._id;
|
|
11963
|
+
if (typeof r.id === "string") return r.id;
|
|
11964
|
+
const funnel = r.funnel;
|
|
11965
|
+
if (funnel && typeof funnel === "object") {
|
|
11966
|
+
const f = funnel;
|
|
11967
|
+
if (typeof f._id === "string") return f._id;
|
|
11968
|
+
if (typeof f.id === "string") return f.id;
|
|
11969
|
+
}
|
|
11970
|
+
return void 0;
|
|
11971
|
+
}
|
|
11843
11972
|
function makeExecuteDeps(client, builderClient, locationId2) {
|
|
11844
11973
|
const pipelineApi = async (method, path7, body) => {
|
|
11845
11974
|
const headers = await builderClient.buildHeaders();
|
|
@@ -11850,6 +11979,27 @@ function makeExecuteDeps(client, builderClient, locationId2) {
|
|
|
11850
11979
|
if (!response.ok) {
|
|
11851
11980
|
const text2 = await response.text();
|
|
11852
11981
|
throw new Error(`Pipeline API ${response.status}: ${method} ${path7}
|
|
11982
|
+
${text2.slice(0, 300)}`);
|
|
11983
|
+
}
|
|
11984
|
+
const text = await response.text();
|
|
11985
|
+
if (!text) return {};
|
|
11986
|
+
try {
|
|
11987
|
+
return JSON.parse(text);
|
|
11988
|
+
} catch {
|
|
11989
|
+
return JSON.parse(text.replace(/[\x00-\x1F\x7F]/g, ""));
|
|
11990
|
+
}
|
|
11991
|
+
};
|
|
11992
|
+
const funnelApi = async (method, path7, body) => {
|
|
11993
|
+
const headers = await builderClient.buildHeaders();
|
|
11994
|
+
headers.Origin = "https://app.gohighlevel.com";
|
|
11995
|
+
headers.Referer = "https://app.gohighlevel.com/";
|
|
11996
|
+
const url = `https://backend.leadconnectorhq.com/funnels${path7}`;
|
|
11997
|
+
const options = { method, headers };
|
|
11998
|
+
if (body && (method === "POST" || method === "PUT")) options.body = JSON.stringify(body);
|
|
11999
|
+
const response = await fetch(url, options);
|
|
12000
|
+
if (!response.ok) {
|
|
12001
|
+
const text2 = await response.text();
|
|
12002
|
+
throw new Error(`Funnel API ${response.status}: ${method} ${path7}
|
|
11853
12003
|
${text2.slice(0, 300)}`);
|
|
11854
12004
|
}
|
|
11855
12005
|
const text = await response.text();
|
|
@@ -11959,6 +12109,49 @@ ${text2.slice(0, 300)}`);
|
|
|
11959
12109
|
}
|
|
11960
12110
|
throw err;
|
|
11961
12111
|
}
|
|
12112
|
+
},
|
|
12113
|
+
// ── Funnels ────────────────────────────────────────────────────────────
|
|
12114
|
+
// Internal funnel API. Origin/Referer are REQUIRED on writes (else 401 "Error
|
|
12115
|
+
// calling IAM service") — mirrors funnel-builder.ts.
|
|
12116
|
+
// Paginate so never-clobber sees ALL funnels (a single page would miss a
|
|
12117
|
+
// same-named funnel beyond it → duplicate). Dedup by id and stop when a page
|
|
12118
|
+
// adds nothing new — robust whether or not the endpoint honors `offset`.
|
|
12119
|
+
listFunnels: async () => {
|
|
12120
|
+
const byId = /* @__PURE__ */ new Map();
|
|
12121
|
+
const pageSize = 100;
|
|
12122
|
+
const maxFunnels = 5e3;
|
|
12123
|
+
for (let offset = 0; offset < maxFunnels; offset += pageSize) {
|
|
12124
|
+
const page = pickFunnels(await funnelApi("GET", `/funnel/list?locationId=${locationId2}&limit=${pageSize}&offset=${offset}&getStats=true`));
|
|
12125
|
+
let added = 0;
|
|
12126
|
+
for (const f of page) if (!byId.has(f.id)) {
|
|
12127
|
+
byId.set(f.id, f);
|
|
12128
|
+
added++;
|
|
12129
|
+
}
|
|
12130
|
+
if (page.length < pageSize || added === 0) break;
|
|
12131
|
+
}
|
|
12132
|
+
return [...byId.values()];
|
|
12133
|
+
},
|
|
12134
|
+
createFunnel: async (name) => {
|
|
12135
|
+
const result = await funnelApi("POST", `/funnel/create?locationId=${locationId2}`, {
|
|
12136
|
+
name,
|
|
12137
|
+
locationId: locationId2,
|
|
12138
|
+
type: "funnel",
|
|
12139
|
+
steps: []
|
|
12140
|
+
});
|
|
12141
|
+
const id = extractFunnelId(result);
|
|
12142
|
+
if (!id) throw new Error(`create_funnel returned no id: ${JSON.stringify(result).slice(0, 200)}`);
|
|
12143
|
+
return id;
|
|
12144
|
+
},
|
|
12145
|
+
createFunnelStep: async (funnelId, name, url) => {
|
|
12146
|
+
const stepId = crypto.randomUUID();
|
|
12147
|
+
await funnelApi("POST", `/funnel/create-step`, {
|
|
12148
|
+
funnelId,
|
|
12149
|
+
step: { id: stepId, name, url, pages: [], control_traffic: 100, split: false, type: "optin_funnel_page" }
|
|
12150
|
+
});
|
|
12151
|
+
return stepId;
|
|
12152
|
+
},
|
|
12153
|
+
deleteFunnel: async (funnelId) => {
|
|
12154
|
+
await funnelApi("POST", `/funnel/delete`, { funnelId, locationId: locationId2, userId: builderClient.getUserId() });
|
|
11962
12155
|
}
|
|
11963
12156
|
};
|
|
11964
12157
|
}
|
|
@@ -12017,7 +12210,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12017
12210
|
);
|
|
12018
12211
|
server2.tool(
|
|
12019
12212
|
"apply_build_plan",
|
|
12020
|
-
`Take an APPROVED Blueprint \xA75 build plan + the CURRENT confirmed sub-account and build it. mode:"dry_run" (default) writes NOTHING \u2014 it resolves refs, expands each workflow's logical actions to native GHL JSON, runs the NEVER-CLOBBER existing-asset scan, and returns a two-part report. Run it FIRST. mode:"execute" performs LIVE writes for the CRM backbone (pipelines+stages, custom fields, tags, custom values), calendars, AND forms: never clobbers (same-named objects are bound to their existing id, never modified), verifies each create by read-back before resolving its ref, halts on the first failure returning the partial idMap, and is idempotent (re-run = no-op). Staff-requiring calendars (round_robin etc.) auto-build only when you are the sole account user (auto-assigns you as the team member); with 0 or 2+ users they're surfaced as a manual step, not auto-staffed to a guess. Forms build with their standard + custom fields (custom fieldRefs resolve to the real fields created earlier in the run). Funnels/
|
|
12213
|
+
`Take an APPROVED Blueprint \xA75 build plan + the CURRENT confirmed sub-account and build it. mode:"dry_run" (default) writes NOTHING \u2014 it resolves refs, expands each workflow's logical actions to native GHL JSON, runs the NEVER-CLOBBER existing-asset scan, and returns a two-part report. Run it FIRST. mode:"execute" performs LIVE writes for the CRM backbone (pipelines+stages, custom fields, tags, custom values), calendars, AND forms: never clobbers (same-named objects are bound to their existing id, never modified), verifies each create by read-back before resolving its ref, halts on the first failure returning the partial idMap, and is idempotent (re-run = no-op). Staff-requiring calendars (round_robin etc.) auto-build only when you are the sole account user (auto-assigns you as the team member); with 0 or 2+ users they're surfaced as a manual step, not auto-staffed to a guess. Forms build with their standard + custom fields (custom fieldRefs resolve to the real fields created earlier in the run). Funnels build structurally (funnel + named steps), with each funnel emitting a manual 'design + populate the pages' step (page content/HTML is not auto-built \u2014 plans carry outlines). Workflows are surfaced as manual next steps, not auto-built yet. Always confirms the active location and validates the plan before any write.`,
|
|
12021
12214
|
{
|
|
12022
12215
|
plan: import_zod53.z.record(import_zod53.z.unknown()).describe("The approved \xA75 Build Plan object."),
|
|
12023
12216
|
mode: import_zod53.z.enum(["dry_run", "execute"]).optional().describe("dry_run (default) = resolve/expand/scan/report, no writes. execute = live writes (not yet enabled)."),
|
|
@@ -12092,7 +12285,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12092
12285
|
}
|
|
12093
12286
|
const deps = makeExecuteDeps(client, builderClient, activeLocation);
|
|
12094
12287
|
const exec = await executeBackbone(typedPlan, deps);
|
|
12095
|
-
const
|
|
12288
|
+
const execManualLines = exec.manual.map((m) => `[${m.type}] ${m.reason}`);
|
|
12096
12289
|
const manualLines = result.workflows.flatMap((w) => [
|
|
12097
12290
|
...w.manual.map((m) => `[${w.name}] ${m.reason}`),
|
|
12098
12291
|
...w.needsContent.map((c) => `[${w.name}] ${c.reason}`)
|
|
@@ -12110,9 +12303,9 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12110
12303
|
manual: exec.manual,
|
|
12111
12304
|
idMap: exec.idMap,
|
|
12112
12305
|
deferred: exec.deferred,
|
|
12113
|
-
deferredNote: "execute builds the CRM backbone (pipelines, custom fields, tags, custom values), calendars, and
|
|
12114
|
-
nextManualSteps: [...
|
|
12115
|
-
summary: exec.ok ? `Built ${exec.built.filter((b) => b.status === "created").length} new object(s), bound ${exec.built.filter((b) => b.status === "existing").length} existing.${exec.manual.length ? ` ${exec.manual.length}
|
|
12306
|
+
deferredNote: "execute builds the CRM backbone (pipelines, custom fields, tags, custom values), calendars, forms, and funnels (funnel + named steps) live. Staff-requiring calendars (round_robin etc.) auto-build only when you are the sole user, else manual. Funnel PAGE CONTENT/design is not auto-built (plans carry outlines, not HTML) \u2014 each funnel lists a manual 'design + populate the pages' step. Workflows are planned but NOT auto-built yet.",
|
|
12307
|
+
nextManualSteps: [...execManualLines, ...manualLines, ...handoffLines],
|
|
12308
|
+
summary: exec.ok ? `Built ${exec.built.filter((b) => b.status === "created").length} new object(s), bound ${exec.built.filter((b) => b.status === "existing").length} existing.${exec.manual.length ? ` ${exec.manual.length} item(s) need a manual step (see nextManualSteps).` : ""}${exec.deferred.length ? " Deferred: " + exec.deferred.map((d) => `${d.count} ${d.section}`).join(", ") + " (manual)." : ""}` : `HALTED at ${exec.halted?.atRef} (${exec.halted?.reason}). ${exec.built.length} object(s) were created before the halt \u2014 see idMap to resume or clean up. NEVER-CLOBBER means a re-run will bind those, not duplicate them.`
|
|
12116
12309
|
});
|
|
12117
12310
|
}
|
|
12118
12311
|
const collisions = result.items.filter((i) => i.status === "existing");
|
|
@@ -12150,7 +12343,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12150
12343
|
calendarsManual: result.calendarsManual,
|
|
12151
12344
|
handoffs: result.handoffs,
|
|
12152
12345
|
report,
|
|
12153
|
-
next: 'Review the report. When it looks right, re-run with mode:"execute" to build the CRM backbone
|
|
12346
|
+
next: 'Review the report. When it looks right, re-run with mode:"execute" to build live: the CRM backbone (pipelines, fields, tags, custom values), calendars, forms, and funnels (funnel + named steps). Funnel page content/design and workflows are listed as manual next steps.'
|
|
12154
12347
|
});
|
|
12155
12348
|
} catch (error) {
|
|
12156
12349
|
return errorResponse(error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elitedcs/ghl-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.40.0",
|
|
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",
|