@elitedcs/ghl-mcp 3.42.0 → 3.44.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 +55 -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.44.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({
|
|
@@ -10786,9 +10798,14 @@ var workflowSchema = import_zod52.z.object({
|
|
|
10786
10798
|
actions: import_zod52.z.array(actionSchema).max(40)
|
|
10787
10799
|
// house rule: <=40 actions/workflow
|
|
10788
10800
|
});
|
|
10801
|
+
var HANDOFF_OWNER_LEGACY = {
|
|
10802
|
+
"JERRY-UI": "OPERATOR-UI",
|
|
10803
|
+
"JERRY-EXT": "OPERATOR-EXT",
|
|
10804
|
+
"SASHA": "TEAM"
|
|
10805
|
+
};
|
|
10789
10806
|
var handoffSchema = import_zod52.z.object({
|
|
10790
10807
|
ref: nsRef("handoff"),
|
|
10791
|
-
owner: import_zod52.z.enum(["JERRY-UI", "JERRY-EXT", "SASHA"]),
|
|
10808
|
+
owner: import_zod52.z.enum(["OPERATOR-UI", "OPERATOR-EXT", "TEAM", "JERRY-UI", "JERRY-EXT", "SASHA"]).transform((o) => HANDOFF_OWNER_LEGACY[o] ?? o),
|
|
10792
10809
|
title: import_zod52.z.string(),
|
|
10793
10810
|
trigger: import_zod52.z.string().optional(),
|
|
10794
10811
|
instruction: import_zod52.z.string(),
|
|
@@ -11006,6 +11023,14 @@ function validateBuildPlan(input) {
|
|
|
11006
11023
|
}
|
|
11007
11024
|
}
|
|
11008
11025
|
}
|
|
11026
|
+
for (const fn of plan.funnels ?? []) {
|
|
11027
|
+
const isExternal = fn.target === "external";
|
|
11028
|
+
if (!isExternal && (fn.host !== void 0 || fn.domain !== void 0)) {
|
|
11029
|
+
warnings.push(
|
|
11030
|
+
`funnel "${fn.ref}" sets host/domain but target is not "external" \u2014 those are ignored for a GHL-built funnel`
|
|
11031
|
+
);
|
|
11032
|
+
}
|
|
11033
|
+
}
|
|
11009
11034
|
for (const p of plan.pipelines ?? []) {
|
|
11010
11035
|
const positions = p.stages.map((s) => s.position).sort((a, b) => a - b);
|
|
11011
11036
|
const expected = positions.every((pos, idx) => pos === idx);
|
|
@@ -11446,7 +11471,10 @@ var SECTION_OBJECTS = {
|
|
|
11446
11471
|
customValues: (p) => (p.customValues ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11447
11472
|
calendars: (p) => (p.calendars ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11448
11473
|
forms: (p) => (p.forms ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11449
|
-
funnels
|
|
11474
|
+
// EXTERNAL funnels are NOT built in GHL (the user hosts them) — exclude them
|
|
11475
|
+
// from the scan so dry-run matches execute: they aren't counted as would-create,
|
|
11476
|
+
// don't seed idMap, and can't trip onConflict on a GHL-funnel name collision.
|
|
11477
|
+
funnels: (p) => (p.funnels ?? []).filter((x) => x.target !== "external").map((x) => ({ ref: x.ref, name: x.name })),
|
|
11450
11478
|
emails: (p) => (p.emails ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11451
11479
|
sms: (p) => (p.sms ?? []).map((x) => ({ ref: x.ref, name: x.name })),
|
|
11452
11480
|
workflows: (p) => (p.workflows ?? []).map((x) => ({ ref: x.ref, name: x.name }))
|
|
@@ -11534,12 +11562,13 @@ function renderReport(plan, result, ctx) {
|
|
|
11534
11562
|
);
|
|
11535
11563
|
L.push("");
|
|
11536
11564
|
const manualCalRefs = new Set(result.calendarsManual.map((c) => c.ref));
|
|
11565
|
+
const externalFunnelRefs = new Set((plan.funnels ?? []).filter((f) => f.target === "external").map((f) => f.ref));
|
|
11537
11566
|
L.push("\u2500\u2500 Blueprint builds automatically \u2500\u2500");
|
|
11538
11567
|
for (const section2 of EXECUTION_ORDER) {
|
|
11539
11568
|
const secItems = result.items.filter((i) => i.type === section2);
|
|
11540
11569
|
if (secItems.length === 0) continue;
|
|
11541
11570
|
for (const it of secItems) {
|
|
11542
|
-
if (manualCalRefs.has(it.ref)) continue;
|
|
11571
|
+
if (manualCalRefs.has(it.ref) || externalFunnelRefs.has(it.ref)) continue;
|
|
11543
11572
|
const mark = it.status === "existing" ? "skip (exists)" : ctx.mode === "dry_run" ? "would create" : "create";
|
|
11544
11573
|
L.push(` [${section2}] ${it.name} \u2014 ${mark}${it.existingId ? ` \u2192 ${it.existingId}` : ""}`);
|
|
11545
11574
|
}
|
|
@@ -11558,7 +11587,11 @@ function renderReport(plan, result, ctx) {
|
|
|
11558
11587
|
}
|
|
11559
11588
|
for (const fn of plan.funnels ?? []) {
|
|
11560
11589
|
any = true;
|
|
11561
|
-
|
|
11590
|
+
if (fn.target === "external") {
|
|
11591
|
+
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.`);
|
|
11592
|
+
} else {
|
|
11593
|
+
L.push(` \u2022 [funnel] Design + populate the pages of funnel "${fn.name}" (Blueprint builds the funnel + steps; page content/design is manual).`);
|
|
11594
|
+
}
|
|
11562
11595
|
}
|
|
11563
11596
|
for (const w of result.workflows) {
|
|
11564
11597
|
if (w.triggerManual) {
|
|
@@ -11808,7 +11841,21 @@ async function executeBackbone(plan, deps, opts = {}) {
|
|
|
11808
11841
|
});
|
|
11809
11842
|
return `Design + publish the pages of funnel "${fn.name}": ${pageBits.join("; ")}. (Blueprint built the funnel + steps; page content/design is manual.)`;
|
|
11810
11843
|
};
|
|
11844
|
+
const externalFunnelReason = (fn) => {
|
|
11845
|
+
const host = fn.host ?? "cloudflare";
|
|
11846
|
+
const bits = fn.pages.map((pg) => {
|
|
11847
|
+
const hosts = [];
|
|
11848
|
+
if (pg.formRef) hosts.push(`form "${formNameByRef.get(pg.formRef) ?? pg.formRef}"`);
|
|
11849
|
+
if (pg.calendarRef) hosts.push(`booking \u2192 calendar "${calNameByRef.get(pg.calendarRef) ?? pg.calendarRef}"`);
|
|
11850
|
+
return `page "${pg.name}"${hosts.length ? ` (${hosts.join(", ")})` : ""}`;
|
|
11851
|
+
});
|
|
11852
|
+
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("; ")}.`;
|
|
11853
|
+
};
|
|
11811
11854
|
for (const fn of plan.funnels ?? []) {
|
|
11855
|
+
if (fn.target === "external") {
|
|
11856
|
+
manual.push({ ref: fn.ref, type: "funnel-external", name: fn.name, reason: externalFunnelReason(fn) });
|
|
11857
|
+
continue;
|
|
11858
|
+
}
|
|
11812
11859
|
let funnels;
|
|
11813
11860
|
try {
|
|
11814
11861
|
funnels = await deps.listFunnels();
|
|
@@ -12415,7 +12462,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12415
12462
|
);
|
|
12416
12463
|
server2.tool(
|
|
12417
12464
|
"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
|
|
12465
|
+
`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
12466
|
{
|
|
12420
12467
|
plan: import_zod53.z.record(import_zod53.z.unknown()).describe("The approved \xA75 Build Plan object."),
|
|
12421
12468
|
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 +12555,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12508
12555
|
manual: exec.manual,
|
|
12509
12556
|
idMap: exec.idMap,
|
|
12510
12557
|
deferred: exec.deferred,
|
|
12511
|
-
deferredNote:
|
|
12558
|
+
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
12559
|
nextManualSteps: [...execManualLines, ...manualLines, ...handoffLines],
|
|
12513
12560
|
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
12561
|
});
|
|
@@ -12550,7 +12597,7 @@ function registerIntakeToBuildTools(server2, client, builderClient) {
|
|
|
12550
12597
|
calendarsManual: result.calendarsManual,
|
|
12551
12598
|
handoffs: result.handoffs,
|
|
12552
12599
|
report,
|
|
12553
|
-
next:
|
|
12600
|
+
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
12601
|
});
|
|
12555
12602
|
} catch (error) {
|
|
12556
12603
|
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.44.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",
|