@elitedcs/ghl-mcp 3.42.0 → 3.43.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 +49 -7
- 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.43.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",
|
|
@@ -10704,9 +10704,21 @@ var pageSchema = import_zod52.z.object({
|
|
|
10704
10704
|
formRef: nsRef("form").optional(),
|
|
10705
10705
|
calendarRef: nsRef("calendar").optional()
|
|
10706
10706
|
});
|
|
10707
|
+
var FUNNEL_TARGETS = ["ghl", "external"];
|
|
10708
|
+
var FUNNEL_HOSTS = ["cloudflare", "vercel"];
|
|
10707
10709
|
var funnelSchema = import_zod52.z.object({
|
|
10708
10710
|
ref: nsRef("funnel"),
|
|
10709
10711
|
name: import_zod52.z.string(),
|
|
10712
|
+
// Where the funnel is built. "ghl" (default) = funnel + named steps in GHL.
|
|
10713
|
+
// "external" = the subscriber builds + hosts the site themselves (Cloudflare/
|
|
10714
|
+
// Vercel) and wires its form back to this GHL sub-account (POWER-USER path —
|
|
10715
|
+
// see blueprint-funnel-targets-spec.md §9). The executor does NOT build or
|
|
10716
|
+
// deploy an external funnel; it surfaces the GHL-side wiring info.
|
|
10717
|
+
target: import_zod52.z.enum(FUNNEL_TARGETS).optional(),
|
|
10718
|
+
host: import_zod52.z.enum(FUNNEL_HOSTS).optional(),
|
|
10719
|
+
// external only
|
|
10720
|
+
domain: import_zod52.z.string().optional(),
|
|
10721
|
+
// external only
|
|
10710
10722
|
pages: import_zod52.z.array(pageSchema)
|
|
10711
10723
|
});
|
|
10712
10724
|
var emailAssetSchema = import_zod52.z.object({
|
|
@@ -11006,6 +11018,14 @@ function validateBuildPlan(input) {
|
|
|
11006
11018
|
}
|
|
11007
11019
|
}
|
|
11008
11020
|
}
|
|
11021
|
+
for (const fn of plan.funnels ?? []) {
|
|
11022
|
+
const isExternal = fn.target === "external";
|
|
11023
|
+
if (!isExternal && (fn.host !== void 0 || fn.domain !== void 0)) {
|
|
11024
|
+
warnings.push(
|
|
11025
|
+
`funnel "${fn.ref}" sets host/domain but target is not "external" \u2014 those are ignored for a GHL-built funnel`
|
|
11026
|
+
);
|
|
11027
|
+
}
|
|
11028
|
+
}
|
|
11009
11029
|
for (const p of plan.pipelines ?? []) {
|
|
11010
11030
|
const positions = p.stages.map((s) => s.position).sort((a, b) => a - b);
|
|
11011
11031
|
const expected = positions.every((pos, idx) => pos === idx);
|
|
@@ -11446,7 +11466,10 @@ var SECTION_OBJECTS = {
|
|
|
11446
11466
|
customValues: (p) => (p.customValues ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11447
11467
|
calendars: (p) => (p.calendars ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11448
11468
|
forms: (p) => (p.forms ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11449
|
-
funnels
|
|
11469
|
+
// EXTERNAL funnels are NOT built in GHL (the user hosts them) — exclude them
|
|
11470
|
+
// from the scan so dry-run matches execute: they aren't counted as would-create,
|
|
11471
|
+
// don't seed idMap, and can't trip onConflict on a GHL-funnel name collision.
|
|
11472
|
+
funnels: (p) => (p.funnels ?? []).filter((x) => x.target !== "external").map((x) => ({ ref: x.ref, name: x.name })),
|
|
11450
11473
|
emails: (p) => (p.emails ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11451
11474
|
sms: (p) => (p.sms ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11452
11475
|
workflows: (p) => (p.workflows ?? []).map((x) => ({ ref: x.ref, name: x.name }))
|
|
@@ -11534,12 +11557,13 @@ function renderReport(plan, result, ctx) {
|
|
|
11534
11557
|
);
|
|
11535
11558
|
L.push("");
|
|
11536
11559
|
const manualCalRefs = new Set(result.calendarsManual.map((c) => c.ref));
|
|
11560
|
+
const externalFunnelRefs = new Set((plan.funnels ?? []).filter((f) => f.target === "external").map((f) => f.ref));
|
|
11537
11561
|
L.push("\u2500\u2500 Blueprint builds automatically \u2500\u2500");
|
|
11538
11562
|
for (const section2 of EXECUTION_ORDER) {
|
|
11539
11563
|
const secItems = result.items.filter((i) => i.type === section2);
|
|
11540
11564
|
if (secItems.length === 0) continue;
|
|
11541
11565
|
for (const it of secItems) {
|
|
11542
|
-
if (manualCalRefs.has(it.ref)) continue;
|
|
11566
|
+
if (manualCalRefs.has(it.ref) || externalFunnelRefs.has(it.ref)) continue;
|
|
11543
11567
|
const mark = it.status === "existing" ? "skip (exists)" : ctx.mode === "dry_run" ? "would create" : "create";
|
|
11544
11568
|
L.push(` [${section2}] ${it.name} \u2014 ${mark}${it.existingId ? ` \u2192 ${it.existingId}` : ""}`);
|
|
11545
11569
|
}
|
|
@@ -11558,7 +11582,11 @@ function renderReport(plan, result, ctx) {
|
|
|
11558
11582
|
}
|
|
11559
11583
|
for (const fn of plan.funnels ?? []) {
|
|
11560
11584
|
any = true;
|
|
11561
|
-
|
|
11585
|
+
if (fn.target === "external") {
|
|
11586
|
+
L.push(` \u2022 [funnel] "${fn.name}" is EXTERNAL (host: ${fn.host ?? "cloudflare"}) \u2014 build + host the site yourself and wire its form to this GHL sub-account (contacts API + the workflow trigger tag); booking \u2192 the GHL calendar URL. Power-user path.`);
|
|
11587
|
+
} else {
|
|
11588
|
+
L.push(` \u2022 [funnel] Design + populate the pages of funnel "${fn.name}" (Blueprint builds the funnel + steps; page content/design is manual).`);
|
|
11589
|
+
}
|
|
11562
11590
|
}
|
|
11563
11591
|
for (const w of result.workflows) {
|
|
11564
11592
|
if (w.triggerManual) {
|
|
@@ -11808,7 +11836,21 @@ async function executeBackbone(plan, deps, opts = {}) {
|
|
|
11808
11836
|
});
|
|
11809
11837
|
return `Design + publish the pages of funnel "${fn.name}": ${pageBits.join("; ")}. (Blueprint built the funnel + steps; page content/design is manual.)`;
|
|
11810
11838
|
};
|
|
11839
|
+
const externalFunnelReason = (fn) => {
|
|
11840
|
+
const host = fn.host ?? "cloudflare";
|
|
11841
|
+
const bits = fn.pages.map((pg) => {
|
|
11842
|
+
const hosts = [];
|
|
11843
|
+
if (pg.formRef) hosts.push(`form "${formNameByRef.get(pg.formRef) ?? pg.formRef}"`);
|
|
11844
|
+
if (pg.calendarRef) hosts.push(`booking \u2192 calendar "${calNameByRef.get(pg.calendarRef) ?? pg.calendarRef}"`);
|
|
11845
|
+
return `page "${pg.name}"${hosts.length ? ` (${hosts.join(", ")})` : ""}`;
|
|
11846
|
+
});
|
|
11847
|
+
return `Funnel "${fn.name}" is EXTERNAL (host: ${host}${fn.domain ? `, domain: ${fn.domain}` : ""}) \u2014 Blueprint does NOT build or deploy it. Build + host the site yourself (technically-capable path), then wire its form to THIS GHL sub-account: upsert the contact via the GHL contacts API (your Private Integration token as a host-side secret) with the plan's custom fields, then add the tag that triggers your speed-to-lead workflow; ${bits.join("; ")}.`;
|
|
11848
|
+
};
|
|
11811
11849
|
for (const fn of plan.funnels ?? []) {
|
|
11850
|
+
if (fn.target === "external") {
|
|
11851
|
+
manual.push({ ref: fn.ref, type: "funnel-external", name: fn.name, reason: externalFunnelReason(fn) });
|
|
11852
|
+
continue;
|
|
11853
|
+
}
|
|
11812
11854
|
let funnels;
|
|
11813
11855
|
try {
|
|
11814
11856
|
funnels = await deps.listFunnels();
|
|
@@ -12415,7 +12457,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12415
12457
|
);
|
|
12416
12458
|
server2.tool(
|
|
12417
12459
|
"apply_build_plan",
|
|
12418
|
-
`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
|
|
12460
|
+
`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: a GHL funnel (target:"ghl", default) builds structurally (funnel + named steps; page content/HTML is a manual step \u2014 plans carry outlines); a funnel with target:"external" is NOT built or deployed here \u2014 the user builds + hosts the site themselves (Cloudflare/Vercel) and wires its form back to this GHL sub-account (surfaced as a manual wiring step). Workflows build as DRAFT with all their logical actions expanded to native GHL JSON (incl. opportunity create/move steps, re-enabled v3.41.0) and chained; a contact_tag trigger is built automatically, other trigger types are surfaced as a manual step; the operator reviews + publishes. Always confirms the active location and validates the plan before any write.`,
|
|
12419
12461
|
{
|
|
12420
12462
|
plan: import_zod53.z.record(import_zod53.z.unknown()).describe("The approved \xA75 Build Plan object."),
|
|
12421
12463
|
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)."),
|
|
@@ -12508,7 +12550,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12508
12550
|
manual: exec.manual,
|
|
12509
12551
|
idMap: exec.idMap,
|
|
12510
12552
|
deferred: exec.deferred,
|
|
12511
|
-
deferredNote:
|
|
12553
|
+
deferredNote: 'execute builds the WHOLE plan live: CRM backbone (pipelines, custom fields, tags, custom values), calendars, forms, funnels (GHL-built = funnel + named steps), and workflows (DRAFT, with all steps incl. opportunity create/move). Remaining manual steps are surfaced per item: staff-requiring calendars in multi-user accounts, GHL funnel page content/design, EXTERNAL funnels (target:"external" \u2014 the user builds + hosts the site themselves and wires it back; not built here), workflow triggers other than contact_tag, and publishing the DRAFT workflows.',
|
|
12512
12554
|
nextManualSteps: [...execManualLines, ...manualLines, ...handoffLines],
|
|
12513
12555
|
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.`
|
|
12514
12556
|
});
|
|
@@ -12550,7 +12592,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12550
12592
|
calendarsManual: result.calendarsManual,
|
|
12551
12593
|
handoffs: result.handoffs,
|
|
12552
12594
|
report,
|
|
12553
|
-
next:
|
|
12595
|
+
next: `Review the report. When it looks right, re-run with mode:"execute" to build it all live: CRM backbone (pipelines, fields, tags, custom values), calendars, forms, GHL funnels (funnel + named steps), and workflows (DRAFT, with all steps incl. opportunity moves). Manual next steps (see the report's Part 2): GHL funnel page content/design; EXTERNAL funnels (target:"external") which you build + host yourself and wire back; workflow triggers other than contact_tag; then publish the workflows.`
|
|
12554
12596
|
});
|
|
12555
12597
|
} catch (error) {
|
|
12556
12598
|
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.43.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",
|